关于python:super().method() 和 super(self.__class__,self).method() 的区别

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().foosuper().foo给出了

1
"SystemError: super(): no arguments".

有人能解释为什么会这样吗?


调用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()