Python继承与类装饰器中super和__init__的正确使用问题
为第三方Widget类添加可选独立行为的实现方案问题
我有多个继承自第三方NotMyWidget类的自定义类,需要为这些类可选添加两种独立行为(可加其一、另一种或两者都加)。若使用传统继承实现会出现菱形继承结构:
class NotMyWidget: pass class OptionalBehavior1(NotMyWidget): pass class OptionalBehavior2(NotMyWidget): pass class MySpecialWidget(OptionalBehavior1, OptionalBehavior2): pass class MySpecialWidget2(OptionalBehavior2): pass
我不确定这是否是最优方案,也不清楚菱形继承是否真的没问题。需要在Python 2.7.5环境下实现合理方案(因依赖包限制无法升级到Python3),同时也希望了解Python3的实现方式。
我尝试使用类装饰器的方式实现,代码如下:
import functools class NotMyWidget: def __init__(self, parent): pass def optional_behavior1(cls): if not issubclass(cls, NotMyWidget): raise TypeError('Decorated class must inherit from NotMyWidget') @functools.wraps(cls, updated=()) class Optional1(cls): def __init__(self, *args, **kwargs): super(Optional1, self).__init__(*args, **kwargs) # optional_behavior1 的特殊初始化逻辑 # 定义重载方法以修改 NotMyWidget 的基础行为 return Optional1 def optional_behavior2(cls): if not issubclass(cls, NotMyWidget): raise TypeError('Decorated class must inherit from NotMyWidget') @functools.wraps(cls, updated=()) class Optional2(cls): def __init__(self, parent, second_arg, *args, **kwargs): super(Optional2, self).__init__(parent, second_arg, *args, **kwargs) # optional_behavior2 的特殊初始化逻辑,包含基于 second_arg 的处理 # 定义重载方法以修改 NotMyWidget 和 MySpecialWidget* 的行为(依赖 second_arg) return Optional2 @optional_behavior2 @optional_behavior1 class MySpecialWidget(NotMyWidget): def __init__(self, parent, second_arg, third_arg): # 尝试1 super(MySpecialWidget, self).__init__(parent) # -> TypeError: __init__() takes exactly 4 arguments (2 given) # 尝试2 super(MySpecialWidget, self).__init__(parent, second_arg, third_arg) # -> RuntimeError: maximum recursion depth exceeded while calling a Python object # (Optional2 __init__ 执行一次,Optional1 __init__ 递归调用直到报错;调换装饰器顺序会反转哪个类执行一次或报错) # 尝试3 # 不调用 super/__init__ # -> RuntimeError: super-class __init__() of type MySpecialWidget was never called # 尝试4 # 此处不调用 super/__init__ # 在 Optional1 和 Optional2 的 __init__ 中先调用 super(cls, self).__init__(parent) # -> TypeError: __init__() takes exactly 4 arguments (2 given) # 尝试5 # 此处不调用 super/__init__ # 在 Optional1 和 Optional2 的 __init__ 中先调用 super(cls, self).__init__(传递与自身 super/__init__ 相同的参数) # -> RuntimeError: super-class __init__() of type MySpecialWidget was never called
曾尝试第4或5种变体并意外成功,但觉得不在MySpecialWidget的__init__中调用super/__init__非常奇怪,之后再尝试就无法复现,显然我对Python继承及super/__init__的机制存在误解,寻求正确的实现方案。
解决方案
一、Python 2.7.5 实现方案
Python2中需注意区分新式类(继承object)与经典类,新式类的C3 MRO算法能安全处理菱形继承,以下两种方案均可行:
1. 修正类装饰器的参数传递与super调用
核心问题是装饰器生成的子类与原类的__init__参数不匹配,以及递归调用逻辑错误。统一参数传递规则,让每个装饰器只处理自身需要的参数,剩余参数传递给父类:
import functools class NotMyWidget(object): # 必须继承object成为新式类 def __init__(self, parent): self.parent = parent print("NotMyWidget initialized") def optional_behavior1(cls): if not issubclass(cls, NotMyWidget): raise TypeError('Decorated class must inherit from NotMyWidget') @functools.wraps(cls, updated=()) class Optional1(cls): def __init__(self, *args, **kwargs): super(Optional1, self).__init__(*args, **kwargs) self.behavior1_enabled = True print("OptionalBehavior1 initialized") return Optional1 def optional_behavior2(cls): if not issubclass(cls, NotMyWidget): raise TypeError('Decorated class must inherit from NotMyWidget') @functools.wraps(cls, updated=()) class Optional2(cls): def __init__(self, parent, second_arg, *args, **kwargs): super(Optional2, self).__init__(parent, *args, **kwargs) self.second_arg = second_arg self.behavior2_enabled = True print("OptionalBehavior2 initialized with second_arg:", second_arg) return Optional2 # 装饰器顺序:先应用的在继承链底层 @optional_behavior1 @optional_behavior2 class MySpecialWidget(NotMyWidget): def __init__(self, parent, second_arg, third_arg): super(MySpecialWidget, self).__init__(parent, second_arg, third_arg) self.third_arg = third_arg print("MySpecialWidget initialized with third_arg:", third_arg) # 测试 widget = MySpecialWidget("parent_obj", "second_val", "third_val")
说明:
- 装饰器顺序决定继承链:
Optional1继承Optional2,Optional2继承MySpecialWidget,最终形成线性MRO链,避免递归调用。 - 每个
__init__通过*args/**kwargs传递未使用的参数,确保父类能拿到所需参数。
2. 用Mixins(混入类)安全实现菱形继承
只要混入类只实现增量行为,且正确调用super,新式类的C3 MRO能安全处理菱形继承:
class NotMyWidget(object): def __init__(self, parent): self.parent = parent print("NotMyWidget initialized") class OptionalBehavior1Mixin(object): # 混入类不直接继承NotMyWidget def __init__(self, *args, **kwargs): super(OptionalBehavior1Mixin, self).__init__(*args, **kwargs) self.behavior1_enabled = True print("OptionalBehavior1 initialized") class OptionalBehavior2Mixin(object): def __init__(self, parent, second_arg, *args, **kwargs): super(OptionalBehavior2Mixin, self).__init__(parent, *args, **kwargs) self.second_arg = second_arg self.behavior2_enabled = True print("OptionalBehavior2 initialized with second_arg:", second_arg) # 自定义类继承顺序:混入类在前,NotMyWidget在后 class MySpecialWidget(OptionalBehavior1Mixin, OptionalBehavior2Mixin, NotMyWidget): def __init__(self, parent, second_arg, third_arg): super(MySpecialWidget, self).__init__(parent, second_arg, third_arg) self.third_arg = third_arg print("MySpecialWidget initialized with third_arg:", third_arg) class MySpecialWidget2(OptionalBehavior2Mixin, NotMyWidget): def __init__(self, parent, second_arg): super(MySpecialWidget2, self).__init__(parent, second_arg) print("MySpecialWidget2 initialized") # 测试 widget1 = MySpecialWidget("parent", "sec", "third") widget2 = MySpecialWidget2("parent2", "sec2")
说明:
- 混入类不直接继承
NotMyWidget,避免菱形继承的歧义。 - 混入类必须调用
super,确保MRO链上所有__init__都被执行。
二、Python3 实现方案
Python3默认都是新式类,super()无需传入类和实例,实现更简洁:
1. 类装饰器方案
import functools class NotMyWidget: def __init__(self, parent): self.parent = parent print("NotMyWidget initialized") def optional_behavior1(cls): if not issubclass(cls, NotMyWidget): raise TypeError('Decorated class must inherit from NotMyWidget') @functools.wraps(cls) class Optional1(cls): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.behavior1_enabled = True print("OptionalBehavior1 initialized") return Optional1 def optional_behavior2(cls): if not issubclass(cls, NotMyWidget): raise TypeError('Decorated class must inherit from NotMyWidget') @functools.wraps(cls) class Optional2(cls): def __init__(self, parent, second_arg, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.second_arg = second_arg self.behavior2_enabled = True print("OptionalBehavior2 initialized with second_arg:", second_arg) return Optional2 @optional_behavior1 @optional_behavior2 class MySpecialWidget(NotMyWidget): def __init__(self, parent, second_arg, third_arg): super().__init__(parent, second_arg, third_arg) self.third_arg = third_arg print("MySpecialWidget initialized with third_arg:", third_arg) # 测试 widget = MySpecialWidget("parent", "sec", "third")
2. Mixins方案
class NotMyWidget: def __init__(self, parent): self.parent = parent print("NotMyWidget initialized") class OptionalBehavior1Mixin: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.behavior1_enabled = True print("OptionalBehavior1 initialized") class OptionalBehavior2Mixin: def __init__(self, parent, second_arg, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.second_arg = second_arg self.behavior2_enabled = True print("OptionalBehavior2 initialized with second_arg:", second_arg) class MySpecialWidget(OptionalBehavior1Mixin, OptionalBehavior2Mixin, NotMyWidget): def __init__(self, parent, second_arg, third_arg): super().__init__(parent, second_arg, third_arg) self.third_arg = third_arg print("MySpecialWidget initialized") class MySpecialWidget2(OptionalBehavior2Mixin, NotMyWidget): def __init__(self, parent, second_arg): super().__init__(parent, second_arg) print("MySpecialWidget2 initialized") # 测试 widget1 = MySpecialWidget("p", "s", "t") widget2 = MySpecialWidget2("p2", "s2")
关键注意点
- 参数一致性:所有
__init__方法必须通过*args/**kwargs传递未使用的参数,确保MRO链上的每个类都能拿到所需参数。 - 装饰器顺序:装饰器的应用顺序决定继承链顺序,最后应用的装饰器生成的类是最底层父类。
- 新式类:Python2中必须让所有类继承
object,否则MRO为深度优先,易引发菱形继承问题;Python3默认都是新式类,无需额外处理。 super调用:每个覆盖__init__的类都必须调用super,确保整个MRO链的初始化逻辑都被执行。
内容的提问来源于stack exchange,提问作者t0w0i7ne
相关产品推荐
相关产品推荐

