用 super() 装饰子类的 __init__ 方法

Posted

技术标签:

【中文标题】用 super() 装饰子类的 __init__ 方法【英文标题】:Decorating a child class's __init__ method with super() 【发布时间】:2015-12-06 02:22:34 【问题描述】:

我的班级层次结构是这样设置的,每个孩子的__init__()必须将self._init_has_run()设置为False,调用父母的__init__(),然后自己做__init__(),最后将self._init_has_run()设置为@987654327 @。我有以下代码:

class Parent:
    def __init__(self, arg1, arg2):
        pass  # do stuff

    def init(cls, fun):
        def decorated_init(self, *args, **kwargs):
            self._init_has_run = False
            x = super()
            super().__init__(*args, **kwargs)
            fun(self, *args, **kwargs)
            self._init_has_run = True
        return decorated_init

class Child(Parent):
    @Parent.init
    def __init__(self, arg1, arg2):
        pass  # do stuff

由于有许多子类遵循与__init__() 相同的一般模式,而且我不知道如何使用元类,所以我使用装饰器来整合重复逻辑,然后仅将该装饰器应用于所有后代__init__() 方法。

Python 抛出以下内容:

File "filename.py", line 82, in decorated_init
super().__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters

我通过调试器确认self._init_has_run 的切换工作正常并且super() 正在解析为Parent 类,但是当装饰器尝试调用super().__init__(*args, **kwargs) 时,为什么Python 会尝试调用object.__init__()

【问题讨论】:

【参考方案1】:

您可以轻松地使用 元类 来做一些 pre/post-init 的事情。考虑这个例子:

class Meta(type):
    def __new__(meta, *args):
        # This is something like 'class constructor'.
        # It is called once for every new class definition.
        # It sets default value of '_init_has_run' for all new objects.
        # This is analog to `class Foo: _init_has_run = False`:
        # new objects will all have _init_has_run set to False by default.

        cls = super(Parent, meta).__new__(meta, *args)
        cls._init_has_run = False
        return cls

    def __call__(cls, *args, **kwargs):
        # This is called each time you create new object.
        # It will run new object's constructor
        # and change _init_has_run to False.

        obj = type.__call__(cls, *args, **kwargs)
        obj._init_has_run = True
        return obj


class Child:
    __metaclass__ = Meta

    def __init__(self):
        print 'init:', self._init_has_run

    def foo(self):
        print 'foo:', self._init_has_run


a = Child()
a.foo()

a = Child()
a.foo()

输出:

init: False
foo: True
init: False
foo: True

希望这会有所帮助!

【讨论】:

我无法将您的答案标记为已接受,因为您提供的示例没有解决我的问题,但它很有帮助,所以我绝对赞成。谢谢。 佩德罗,你可以描述一下还缺少什么,我会努力改进我的答案;)

以上是关于用 super() 装饰子类的 __init__ 方法的主要内容,如果未能解决你的问题,请参考以下文章

Python3基础 super层层调用父类的__init__方法 子类的__init__覆盖了父类的__init__的解决方法

用正确的名字装饰一个类

Python3基础 super 子类调用父类的__init__

Python3基础 super 子类调用父类的__init__

super()方法的使用

用super为多个父类调用init? [复制]