You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

子类继承时如何自动生成与自定义正则匹配的ClassTuple?

自动为子类生成匹配正则分组的ClassTuple

问题核心在于:基类的ClassTuple是在基类定义阶段生成的,子类继承时会直接复用基类的ClassTuple,不会自动根据子类的新PATTERN更新,导致分组不匹配的错误。以下是两种简洁的解决方案,无需子类重复编写ClassTuple代码:

方案一:使用__init_subclass__(推荐,Python3.6+)

利用Python3.6引入的__init_subclass__方法,在子类定义时自动生成对应正则分组的ClassTuple,代码直观且无需元类:

import re
from collections import namedtuple
from typing import Optional


class MyBaseClass:
    PATTERN = re.compile('(?P<digits>[0-9]+).*(?P<suffix>foo|bar)')
    
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        # 基于子类的PATTERN自动生成ClassTuple
        cls.ClassTuple = namedtuple('ClassTuple', tuple(cls.PATTERN.groupindex))
    
    def __init__(self, string: Optional[str] = None, tup: Optional["ClassTuple"] = None):
        if string is not None:
            mapping = self.PATTERN.fullmatch(string).groupdict()
        elif tup is not None:
            mapping = tup._asdict()
            
        for k, v in mapping.items():
            setattr(self, k, v)


# 子类仅需定义PATTERN
class MySubClass(MyBaseClass):
    PATTERN = re.compile('(?P<letters>[A-Za-z]+).*(?P<suffix>fisk|mask)')


# 测试代码
# 基类测试
bc = MyBaseClass('123_abc_foo')
print(bc.digits, bc.suffix)

bc_tuple = MyBaseClass.ClassTuple(digits='456', suffix='bar')
bc = MyBaseClass(tup=bc_tuple)
print(bc.digits, bc.suffix)

# 子类测试
sc = MySubClass('abc_123_fisk')
print(sc.letters, sc.suffix)

sc_tuple = MySubClass.ClassTuple(letters='def', suffix='mask')
sc = MySubClass(tup=sc_tuple)
print(sc.letters, sc.suffix)

运行结果:

123 foo
456 bar
abc fisk
def mask

原理说明

__init_subclass__会在每个子类定义完成后自动触发,此时可以直接访问子类的PATTERN属性,根据其分组名动态生成对应的namedtuple并绑定到子类的ClassTuple上。如果子类未定义PATTERN,则会继承基类的PATTERN并生成对应ClassTuple,保持基类行为一致。

方案二:使用元类(兼容旧版本Python)

如果需要兼容Python3.6之前的版本,可以用元类实现相同逻辑:

import re
from collections import namedtuple
from typing import Optional


class PatternTupleMeta(type):
    def __new__(cls, name, bases, attrs):
        new_cls = super().__new__(cls, name, bases, attrs)
        # 获取子类或继承的PATTERN
        pattern = attrs.get('PATTERN', getattr(new_cls, 'PATTERN', None))
        if pattern:
            new_cls.ClassTuple = namedtuple('ClassTuple', tuple(pattern.groupindex))
        return new_cls


class MyBaseClass(metaclass=PatternTupleMeta):
    PATTERN = re.compile('(?P<digits>[0-9]+).*(?P<suffix>foo|bar)')
    
    def __init__(self, string: Optional[str] = None, tup: Optional["ClassTuple"] = None):
        if string is not None:
            mapping = self.PATTERN.fullmatch(string).groupdict()
        elif tup is not None:
            mapping = tup._asdict()
            
        for k, v in mapping.items():
            setattr(self, k, v)


# 子类使用方式不变
class MySubClass(MyBaseClass):
    PATTERN = re.compile('(?P<letters>[A-Za-z]+).*(?P<suffix>fisk|mask)')

原理说明

元类的__new__方法会在类创建时被调用,我们可以在此阶段动态修改类的属性,为子类生成对应的ClassTuple。元类的理解成本稍高,但兼容性更好。

适配实际场景

如果你的基类还有其他依赖ClassTuple的代码,只要这些代码是在子类定义完成后(比如实例化时)调用,都会自动使用子类生成的ClassTuple,无需额外修改。

内容的提问来源于stack exchange,提问作者Stalpotaten

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 05:18:25