8

这是我试图编写的代码:

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

"SystemError: super(): no arguments". 

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

4

1 回答 1

26

你不应该调用时使用self.__class__或。type(self)super()

在 Python 3 中,super()不带参数的调用等同于super(B, self)(within methods on class B); 注意类的显式命名。Python 编译器为不带参数__class__使用的方法添加了一个闭包单元(请参阅Python 3.x 的 super() 为何如此神奇?),它引用了当前定义的类。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()

等于:

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

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

但是您应该使用前者,因为它可以节省您重复自己的时间。

在 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

可以使用__class__(no self) 让 Python 为您提供闭包单元,但这将是对 的引用A,而不是C此处。当您绑定 newfoo时,您希望在 MRO 中搜索被覆盖的方法,C而不是开始搜索。

请注意,如果您现在创建一个类D,从 子类化,事情将再次C出错,因为现在您正在调用,然后又以, not为起点调用。那么最好的选择是使用显式的类引用进行调用。这里(no ) 会做得很好:bind_foo()super()DCbind_foo()__class__self.

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

super()现在您的行为与不带参数使用的行为相同,对当前类的引用(您在其中定义方法的类__init__)被传递给super(),从而使new_foo()行为就好像它是直接在 的类定义中定义的一样C

请注意,bind_foo()super()这里调用是没有意义的;你没有在这里覆盖它,所以你可以打电话self.bind_foo()来代替。

于 2013-11-04T20:07:03.290 回答