调用超级初始化的python装饰器
Posted
技术标签:
【中文标题】调用超级初始化的python装饰器【英文标题】:python decorator that calls super init 【发布时间】:2018-05-04 00:36:38 【问题描述】:我正在尝试制作一个自动调用父类 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
【问题讨论】:
看看第二种情况下self.__class__
是什么。它将永远是Parent
,因为self
永远不会停止成为Child
的实例,无论您调用什么方法。
您希望引用包含您正在调用的__init
的类。这将确保您的递归最终终止。
这个装饰器到底有什么意义???
不要费心让这个装饰器工作。这将是一段充满陷阱的艰难旅程。只需像其他人一样在构造函数中调用父 __init__
。无论如何,它只比输入@init
长几个字符。
@Rawing。我希望我在回答中抓住了你的情绪。
【参考方案1】:
正确的方式
执行此操作的正确方法是直接在代码中调用父初始化程序。在 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
。无论如何,它是同一个想法的一个更好的深思熟虑的版本。
【讨论】:
以上是关于调用超级初始化的python装饰器的主要内容,如果未能解决你的问题,请参考以下文章