【问题标题】:Decorating a child class's __init__ method with super()用 super() 装饰子类的 __init__ 方法
【发布时间】: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__()

【问题讨论】:

    标签: python python-3.x decorator python-decorators


    【解决方案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
    

    希望这会有所帮助!

    【讨论】:

    • 我无法将您的答案标记为已接受,因为您提供的示例没有解决我的问题,但它很有帮助,所以我绝对赞成。谢谢。
    • 佩德罗,你可以描述一下还缺少什么,我会努力改进我的答案;)
    猜你喜欢
    • 2019-12-20
    • 2013-05-23
    • 2014-01-14
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    相关资源
    最近更新 更多