super().method() 和 super(self.__class__,self).method() 的区别

问题描述:

这是我试图编写的代码:

Here is the code I was trying to write:

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() 被调用.它按预期工作.

Class B and A is the expected behavior, that is, when I call b.foo(), it calls a.foo() as well with super(). Class C is the trying to mimic the child B and parent A behavior but this time I dont want to put explicitly super().foo() in the child class but I still want the parent foo() to be called. It works as expected.

然而,我不太明白的是,在 A.bind_foo 下,我必须使用 super(self.__class__,self).foo() 而不是super().foo.super().foo 给出一个

However, what I dont quite get is that, under A.bind_foo, I have to use super(self.__class__,self).foo() rather than super().foo. super().foo gives a

"SystemError: super(): no arguments". 

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

Can someone explain why that is so?

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

You should not use self.__class__ or type(self) when calling super().

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

In Python 3, a call to super() without arguments is equivalent to super(B, self) (within methods on class B); note the explicit naming of the class. The Python compiler adds a __class__ closure cell to methods that use super() without arguments (see Why is Python 3.x's super() magic?) that references the current class being defined.

如果你使用super(self.__class__, self)super(type(self), self),你会在子类尝试时遇到无限递归异常调用该方法;那个时候self.__class__派生类,不是原来的.参见 当调用 super() 时在派生类中,我可以传入 self.__class__ 吗?

If you use super(self.__class__, self) or super(type(self), self), you will hit an infinite recursion exception when a subclass tries to call that method; at that time self.__class__ is the derived class, not the original. See When calling super() in a derived class, can I pass in self.__class__?

总结一下,在 Python 3 中:

So, to summarize, in Python 3:

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

    def foo(self):
        print("B foo")
        super().foo()

等于:

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

    def foo(self):
        print("B foo")
        super(B, self).foo()

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

but you should use the former, as it saves you repeating yourself.

在 Python 2 中,您只使用第二种形式.

In Python 2, you are stuck with the second form only.

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

For your bind_foo() method, you'll have to pass in an explicit class from which to search the MRO from, as the Python compiler cannot determine here what class is used when you bind the new replacement 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

可以使用__class__(没有self)让Python为你提供闭包单元,但这将是对A,不是 C 这里.当您绑定新的 foo 时,您希望在 MRO 中搜索被覆盖的方法,而不是从 C 开始搜索.

You could use __class__ (no self) to have Python provide you with the closure cell, but that'd be a reference to A, not C here. When you are binding the new foo, you want the search for overridden methods in the MRO to start searching at C instead.

请注意,如果您现在创建一个 D 类,从 C 子类化,事情会再次出错,因为现在您正在调用 bind_foo() 并依次以 D 而不是 C 为起点调用 super().最好的办法是使用 显式 类引用调用 bind_foo().这里 __class__(没有 self.)会做得很好:

Note that if you now create a class D, subclassing from C, things will go wrong again, because now you are calling bind_foo() and in turn call super() with D, not C, as the starting point. Your best bet then is to call bind_foo() with an explicit class reference. Here __class__ (no self.) will do nicely:

class C(A):
    def __init__(self):
        print("C __init__")
        super().__init__()
        self.bind_foo(__class__)

现在您的行为与不带参数使用 super() 相同,是对 current 类的引用,您在其中定义方法 __init__,被传递给 super(),使得 new_foo() 的行为就像直接在 C 的类定义中定义一样代码>.

Now you have the same behaviour as using super() without arguments, a reference to the current class, the one in which you are defining the method __init__, is passed to super(), making the new_foo() behave as if it was defined directly in the class definition of C.

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

Note that there is no point in calling bind_foo() on super() here; you didn't override it here, so you can just call self.bind_foo() instead.