调用super init的python decorator

时间:2017-11-20 17:40:02

标签: python reflection decorator

我试图制作一个自动调用父类init的装饰器。它适用于单继承,但当我尝试链接效果时,我得到堆栈溢出错误。任何人都可以解释(a)为什么会发生这种情况和(b)如何实现我想要完成的目标?

def init(__init):
    def wrapper(self, *args, **kwargs):
        self.__class__.__bases__[0].__init__(self)
        __init(self)
    return wrapper

class Base:
    def __init__(self):
        print("base init")


class Parent(Base):
    @init
    def __init__(self):
        print("parent init")

class Child(Parent):
    @init
    def __init__(self):
        print("child init")

a = Parent() #works
#c = Child() #stack overflow

1 个答案:

答案 0 :(得分:3)

正确的方式

执行此操作的正确方法是直接在代码中调用父初始值设定项。在Python 3中:

super().__init__()

在Python 2中:

super(Parent, self).__init__()

super(Child, self).__init__()

错误

要回答你当前的问题,无限递归的发生是因为你得到了父母:

self.__class__.__bases__[0]
无论你多少次调用self函数,

Child都不会停止成为wrapper的实例。您最终无限期地调用Parent.__init__(self),因为父类永远不会更改。

错误的方式

您可能想要的是找到定义您当前正在调用的__init__方法的类,并获取其父级。 @AlexMartelli在answer提供了一个函数来执行此操作或Python,我在这里逐字复制:

import inspect

def get_class_that_defined_method(meth):
    for cls in inspect.getmro(meth.im_class):
        if meth.__name__ in cls.__dict__: 
            return cls
    return None

我使用的是Python 3,请改用@Yoel's version

def get_class_that_defined_method(meth):
    if inspect.ismethod(meth):
        for cls in inspect.getmro(meth.__self__.__class__):
           if cls.__dict__.get(meth.__name__) is meth:
                return cls
        meth = meth.__func__  # fallback to __qualname__ parsing
    if inspect.isfunction(meth):
        cls = getattr(inspect.getmodule(meth),
                      meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
        if isinstance(cls, type):
            return cls
    return getattr(meth, '__objclass__', None)  # handle special descriptor objects

您现在可以按如下方式重新定义init功能:

def init(__init):
    def wrapper(self, *args, **kwargs):
        get_class_that_defined_method(__init).__bases__[0].__init__(self)
        __init(self)
    return wrapper

再次。请不要这样做。从__bases__[0]左右的所有事情开始,我可能还有许多我未提及的角落案例。只需使用super代替。无论如何,这是一个更好的思想版本的同一个想法。