The difference between super().method() versus super(self.__class__,self).method()
这是我试图写的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class A(object):
def bind_foo(self):
old_foo = self.foo
def new_foo():
old_foo()
#super().foo()
super(self.__class__,self).foo()
self.foo = new_foo
def __init__(self):
print("A __init__")
def foo(self):
print("A foo")
class B(A):
def __init__(self):
print("B __init__")
super().__init__()
def foo(self):
print("B foo")
super().foo()
class C(A):
def __init__(self):
print("C __init__")
super().__init__()
super().bind_foo()
def foo(self):
print("C foo")
b = B()
b.foo()
c = C()
c.foo() |
B类和A类是预期行为,也就是说,当我调用b.foo()时,它调用a.foo()和super()时。C类是试图模仿孩子B和父母A的行为,但这次我不想把super().foo()显式地放在孩子类中,但我仍然希望调用父母foo()。它按预期工作。
然而,我不太明白的是,在A.bind_foo下,我必须使用super(self.__class__,self).foo()而不是super().foo。super().foo给出了
1
| "SystemError: super(): no arguments". |
有人能解释为什么会这样吗?
- 你在bind_foo所做的是错误的。super(self.__class__, self).foo()实际上是在实际实例的第一个超类上调用foo。所以,如果您有class B(A),然后是class C(B),然后是class D(C),并且执行D().bind_foo(),那么您最终会调用C实现。那不是你想要的。如果要调用D实现,只需调用self.foo()。如果要调用A实现,请调用A.foo(self)。
- 理想情况下,我希望D调用自己的实现,然后是C,然后是B,然后是A。
调用super()时,不应使用self.__class__或type(self)。
在python 3中,对super()的无参数调用等同于super(B, self)(在类B的方法中);注意类的显式命名。python编译器将一个__class__闭包单元添加到使用super()而不带参数的方法中(参见为什么python 3.x的super()有魔力?)引用当前定义的类。
如果使用super(self.__class__, self)或super(type(self), self),当子类试图调用该方法时,将遇到无限递归异常;此时self.__class__是派生类,而不是原始类。在派生类中调用super()时,我可以传入self吗?
总之,在python 3中:
1 2 3 4 5 6 7 8
| class B(A):
def __init__(self):
print("B __init__")
super().__init__()
def foo(self):
print("B foo")
super().foo() |
等于:
1 2 3 4 5 6 7 8
| class B(A):
def __init__(self):
print("B __init__")
super(B, self).__init__()
def foo(self):
print("B foo")
super(B, self).foo() |
但是你应该使用前者,因为它可以避免你重复自己。
在Python2中,您只能使用第二个表单。
对于您的bind_foo()方法,您必须传入一个显式类,从中搜索mro,因为python编译器在这里无法确定绑定新替换的foo时使用的是什么类:
1 2 3 4 5 6 7 8 9 10
| def bind_foo(self, klass=None):
old_foo = self.foo
if klass is None:
klass = type(self)
def new_foo():
old_foo()
super(klass, self).foo()
self.foo = new_foo |
您可以使用__class__(没有self)让python为您提供闭包单元,但这将是对A的引用,而不是这里的C。当您绑定新的foo时,您希望在mro中搜索重写的方法开始搜索C。
注意,如果您现在创建一个类D,从C子类,事情将再次出错,因为现在您调用bind_foo(),而反过来调用super(),以D,而不是C作为起点。最好的选择是使用显式类引用调用bind_foo()。在这里,__class__(没有self.)会做得很好:
1 2 3 4 5
| class C(A):
def __init__(self):
print("C __init__")
super().__init__()
self.bind_foo(__class__) |
现在,您的行为与在没有参数的情况下使用super()相同,当前类的引用(在其中定义方法__init__)被传递给super(),使得new_foo()的行为就像直接在C的类定义中定义的那样。
请注意,在这里调用super()没有意义;您没有在这里覆盖它,所以您可以只调用self.bind_foo()。
- 那么,我该如何解决我手头上的问题,然后与孩子C?
- 我怀疑无参数super在new_foo的定义中不起作用,因为这不是一个方法(只是一个正则函数被分配给一个实例变量)。将继承样式的方法重载(可以在其中调用super)与实例变量重载混合使用可能是个坏主意。
- @BLCKKNGHT:您真的不想在那里使用自动类;您想在调用时确定类(所以当调用bind_foo()时,而不是创建bind_foo()函数对象时)。
- 谢谢你的帮助!假设我希望D与C固有,C与B固有,B与A固有,但我希望D'sfoo调用D's实现,然后调用C's,然后调用B's,然后调用A's。最好的方法是什么?
- @不伤害:通过给每个类一个做正确事情的foo实现;调用super().foo()。另请参阅RaymondHettinger在super()上的博客帖子。这就是super()所说的合作类设计。
- 在每个foo()中不明确地加上super()是不可能的吗?
- @不危害:可以循环访问type(self).mro(),查看每个类是否有foo属性,然后直接调用该函数传入self。你不会在那里使用super(),这不是super()的作用。
- 我不知道江户十一〔二十二〕的存在,这对我们的生活有很大的帮助。谢谢!