调用 super init 的 python 装饰器

标签 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

最佳答案

正确的方法

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

super().__init__()

在 Python 2 中:

super(Parent, self).__init__()

super(Child, self).__init__()

错误

为了回答您直接的问题,无限递归的发生是由于您如何获取父级:

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

self 都不会停止成为 Child 的实例。您最终会无限期地调用 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 即可。无论如何,这是同一想法的一个经过深思熟虑的更好版本。

关于调用 super init 的 python 装饰器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47397998/

相关文章:

python - ctr预测中计算auc的这段代码是什么意思?

python - 如何用相同的代码满足不同的字符串格式化场景?

python - 如何编写时间相关的测试 Python

python - 使用静音检测分割音频文件

c# - 如何调用 Activator.CreateInstance,将方法作为构造函数参数传递?

Java - 使用注释的自定义/标准验证

java - 反射安全

python - 调用另一个装饰器函数的装饰函数会正常返回吗?

python - 确定 Python 函数中返回值的数量

python - 重写的方法是否继承python中的装饰器?