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

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")

关键注意点

  1. 参数一致性:所有__init__方法必须通过*args/**kwargs传递未使用的参数,确保MRO链上的每个类都能拿到所需参数。
  2. 装饰器顺序:装饰器的应用顺序决定继承链顺序,最后应用的装饰器生成的类是最底层父类。
  3. 新式类:Python2中必须让所有类继承object,否则MRO为深度优先,易引发菱形继承问题;Python3默认都是新式类,无需额外处理。
  4. super调用:每个覆盖__init__的类都必须调用super,确保整个MRO链的初始化逻辑都被执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:54:58