在类方法中使用super

103 投票
5 回答
80657 浏览
提问于 2025-04-15 16:30

我正在尝试学习Python中的super()函数。

我以为自己已经掌握了这个概念,但在看到一个例子(2.6)后,我发现自己卡住了。

http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#super-with-classmethod-example

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 9, in do_something
    do_something = classmethod(do_something)
TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead)
>>>

在看到这个例子之前,我读到的这一行让我感到意外:

如果我们使用类方法,就没有实例可以用来调用super。幸运的是,super即使在第二个参数是类型的情况下也能工作。--- 类型可以直接传递给super,如下所示。

这正是Python告诉我的不可能的事情,因为它说do_something()应该用B的一个实例来调用。

5 个回答

4

我更新了这篇文章,让它更清晰一些:Python 属性和方法 # 超级

你上面用 classmethod 的例子展示了什么是类方法——它把类本身作为第一个参数,而不是实例。不过,你甚至不需要实例就可以调用这个方法,比如:

>>> class A(object):
...     @classmethod
...     def foo(cls):
...         print cls
... 
>>> A.foo() # note this is called directly on the class
<class '__main__.A'>
70

在Python 3中,你可以不必为super指定参数,

class A:
    @classmethod
    def f(cls):
        return "A's f was called."

class B(A):
    @classmethod
    def f(cls):
        return super().f()

assert B.f() == "A's f was called."
123

有时候,读一些文本更多是为了理解大概念,而不是细节。这就是这种情况。

这个链接的页面中,例子2.5、2.6和2.7都应该使用同一个方法,叫做do_your_stuff。(也就是说,do_something应该改成do_your_stuff。)

另外,正如Ned Deily指出的A.do_your_stuff必须是一个类方法。

class A(object):
    @classmethod
    def do_your_stuff(cls):
        print 'This is A'

class B(A):
    @classmethod
    def do_your_stuff(cls):
        super(B, cls).do_your_stuff()

B.do_your_stuff()

super(B, cls).do_your_stuff返回一个绑定的方法(见脚注2)。因为cls作为第二个参数传给了super(),所以返回的方法会绑定到cls上。换句话说,cls会作为第一个参数传给类A的do_your_stuff()方法。

再说一遍:super(B, cls).do_your_stuff()会调用Ado_your_stuff方法,并且cls会作为第一个参数传入。为了让这个工作正常,Ado_your_stuff必须是一个类方法。链接的页面没有提到这一点,但这确实是事实。

附注:do_something = classmethod(do_something)是创建类方法的旧方式。现在更常用的方法是使用@classmethod装饰器。


注意,super(B, cls)不能被super(cls, cls)替代。这样做可能会导致无限循环。例如,

class A(object):
    @classmethod
    def do_your_stuff(cls):
        print('This is A')

class B(A):
    @classmethod
    def do_your_stuff(cls):
        print('This is B')
        # super(B, cls).do_your_stuff()  # CORRECT
        super(cls, cls).do_your_stuff()  # WRONG

class C(B):
    @classmethod
    def do_your_stuff(cls):
        print('This is C')
        # super(C, cls).do_your_stuff()  # CORRECT
        super(cls, cls).do_your_stuff()  # WRONG

C.do_your_stuff()

会引发RuntimeError: maximum recursion depth exceeded while calling a Python object

如果clsC,那么super(cls, cls)会在C.mro()中查找在C之后的类。

In [161]: C.mro()
Out[161]: [__main__.C, __main__.B, __main__.A, object]

因为那个类是B,当clsC时,super(cls, cls).do_your_stuff() 总是调用B.do_your_stuff。由于super(cls, cls).do_your_stuff()是在B.do_your_stuff内部被调用的,这样就会导致无限循环。

在Python3中,增加了0个参数的super形式,这样super(B, cls)可以被super()替代,Python3会根据上下文判断在class B的定义中,super()相当于super(B, cls)

但是在任何情况下,super(cls, cls)(或者出于类似原因,super(type(self), self))都是不正确的。

撰写回答