如何继承父类但排除祖父类特性?Python类继承技术问询
实现子类仅继承直接父类特性的优化方案
问题描述
简化示例场景
需要实现子类仅继承直接父类的属性,且父类的__init_subclass__仅对直接子类生效,同时不能修改中间子类的定义:
class A: var: int = 0 def __init_subclass__(cls): print(cls.__name__) class B(A): ... class C(B): ...
要求:
- B继承A的
var变量,C不继承A的var __init_subclass__仅对B生效,输出仅显示"B"- 不能修改类B的定义
实际业务场景
基于dataclass的业务代码中,需要避免间接子类触发祖父类的__init_subclass__逻辑,同时不能调整继承顺序:
from dataclasses import dataclass, field @dataclass class StructModuleBase: TableName: str = field(init = False, repr = False) def __init_subclass__(cls, as_table = None, **kwargs) -> None: cls.TableName = cls.__name__.replace('Struct', '', 1) # 其他业务逻辑 @dataclass class StructSettings(StructModuleBase): key_setting: str value_setting: str @dataclass class DataclassModuleBase: _database: str = field(repr = False) @dataclass class Setting(StructSettings, DataclassModuleBase): ...
需求:
- Setting类继承StructSettings的
key_setting和value_setting属性 - 不触发StructModuleBase中
__init_subclass__方法的执行(该方法仅对StructSettings生效) - 不能改变StructSettings与DataclassModuleBase的继承顺序,无法通过后者覆盖
__init_subclass__
现有解决方案依赖类名检查,健壮性不足:
def __init_subclass__(cls, as_table = None, **kwargs) -> None: if 'Struct' not in cls.__name__: return cls.TableName = cls.__name__.replace('Struct', '', 1)
优化解决方案
方案1:检查直接父类(推荐)
修改StructModuleBase的__init_subclass__方法,通过判断当前类是否属于子类的直接父类,来决定是否执行逻辑:
def __init_subclass__(cls, as_table=None, **kwargs) -> None: # 仅当子类直接继承自当前类时,才执行后续逻辑 if StructModuleBase in cls.__bases__: cls.TableName = cls.__name__.replace('Struct', '', 1) # 原有业务逻辑
原理说明
Python定义子类时,所有父类(包括间接父类)的__init_subclass__都会被调用。cls.__bases__返回子类的直接父类元组:
- StructSettings的
__bases__为(StructModuleBase,),包含StructModuleBase,因此会执行逻辑 - Setting的
__bases__为(StructSettings, DataclassModuleBase),不包含StructModuleBase,因此跳过逻辑
该方案不依赖类命名规则,逻辑更健壮,即使后续类名修改也不会影响功能。
方案2:使用类标记(可选)
如果需要更灵活的控制,可以在直接子类中添加标记属性,在__init_subclass__中检查该标记:
- 在StructModuleBase中定义标记常量:
class StructModuleBase: _DIRECT_SUBCLASS = False # 其他属性和方法
- 修改StructSettings(若允许修改的话,这种方式逻辑更明确):
@dataclass class StructSettings(StructModuleBase): _DIRECT_SUBCLASS = True key_setting: str value_setting: str
- 修改
__init_subclass__:
def __init_subclass__(cls, as_table=None, **kwargs) -> None: if getattr(cls, '_DIRECT_SUBCLASS', False): cls.TableName = cls.__name__.replace('Struct', '', 1) # 原有业务逻辑
注:该方案需要修改StructSettings的定义,若无法修改则优先使用方案1。
内容的提问来源于stack exchange,提问作者EFERYEG
相关产品推荐
相关产品推荐

