我试图制作一个自动调用父类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
答案 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
代替。无论如何,这是一个更好的思想版本的同一个想法。