super().method()与super(self .__ class __,self).method()之间的区别

No *_*ing 4 python inheritance class

这是我试图编写的代码:

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()
Run Code Online (Sandbox Code Playgroud)

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给了一个

"SystemError: super(): no arguments". 
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ers 18

你应该使用self.__class__type(self)调用时super().

在Python 3中,对super()不带参数的调用等同于super(B, self)(在类中的方法内B); 注意类的显式命名.Python编译器__class__为不使用super()参数的方法添加一个闭包单元(请参阅为什么Python 3.x的super()魔术?)引用当前类正在定义.

如果使用super(self.__class__, self)or super(type(self), self),当子类尝试调用该方法时,您将遇到无限递归异常; 那个时候self.__class__派生类,而不是原始.请参阅在派生类中调用super()时,是否可以传入self .__ class__?

那么,总结一下,在Python 3中:

class B(A):
    def __init__(self):
        print("B __init__")
        super().__init__()

    def foo(self):
        print("B foo")
        super().foo()
Run Code Online (Sandbox Code Playgroud)

等于:

class B(A):
    def __init__(self):
        print("B __init__")
        super(B, self).__init__()

    def foo(self):
        print("B foo")
        super(B, self).foo()
Run Code Online (Sandbox Code Playgroud)

但你应该使用前者,因为它可以节省你重复自己.

在Python 2,你被卡住第二种形式.

对于您的bind_foo()方法,您必须传入一个显式类来从中搜索MRO,因为Python编译器无法在此确定绑定新替换时使用的类foo:

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
Run Code Online (Sandbox Code Playgroud)

可以使用__class__(no self)让Python为您提供闭包单元格,但这是一个引用A,而不是C在这里.当您绑定新的时foo,您希望在MRO中搜索重写的方法以开始搜索C.

需要注意的是,如果你现在创建一个类D,从继承C,事情会出错去一次,因为现在您呼叫bind_foo()和呼叫转super()D,而不是C作为出发点.那么您最好的选择是bind_foo()使用明确的类引用.这里__class__(不self.)会做得很好:

class C(A):
    def __init__(self):
        print("C __init__")
        super().__init__()
        self.bind_foo(__class__)
Run Code Online (Sandbox Code Playgroud)

现在,您具有与super()不使用参数相同的行为,对当前类的引用(定义方法的类__init__)将被传递给super(),使得new_foo()行为就好像它是直接在类定义中定义的那样C.

请注意,bind_foo()super()这里呼吁没有意义; 你没有在这里覆盖它,所以你可以调用它self.bind_foo().