在类方法中使用 super

我正在尝试学习 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 的实例来调用,这是不可能的。

67603 次浏览

The example from the web page seems to work as published. Did you create a do_something method for the superclass as well but not make it into a classmethod? Something like this will give you that error:

>>> class A(object):
...     def do_something(cls):
...         print cls
... #   do_something = classmethod(do_something)
...
>>> class B(A):
...     def do_something(cls):
...         super(B, cls).do_something()
...     do_something = classmethod(do_something)
...
>>> B().do_something()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in do_something
TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead)

Sometimes texts have to be read more for the flavor of the idea rather than for the details. This is one of those cases.

In the linked page, Examples 2.5, 2.6 and 2.7 should all use one method, do_your_stuff. (That is, do_something should be changed to do_your_stuff.)

In addition, as Ned Deily pointed out, A.do_your_stuff has to be a class method.

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 returns a bound method (see footnote 2). Since cls was passed as the second argument to super(), it is cls that gets bound to the returned method. In other words, cls gets passed as the first argument to the method do_your_stuff() of class A.

To reiterate: super(B, cls).do_your_stuff() causes A's do_your_stuff method to be called with cls passed as the first argument. In order for that to work, A's do_your_stuff has to be a class method. The linked page doesn't mention that, but that is definitively the case.

PS. do_something = classmethod(do_something) is the old way of making a classmethod. The new(er) way is to use the @classmethod decorator.


Note that super(B, cls) can not be replaced by super(cls, cls). Doing so could lead to infinite loops. For example,

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

will raise RuntimeError: maximum recursion depth exceeded while calling a Python object.

If cls is C, then super(cls, cls) searches C.mro() for the class that comes after C.

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

Since that class is B, when cls is C, super(cls, cls).do_your_stuff() always calls B.do_your_stuff. Since super(cls, cls).do_your_stuff() is called inside B.do_your_stuff, you end up calling B.do_your_stuff in an infinite loop.

In Python3, the 0-argument form of super was added so super(B, cls) could be replaced by super(), and Python3 will figure out from context that super() in the definition of class B should be equivalent to super(B, cls).

But in no circumstance is super(cls, cls) (or for similar reasons, super(type(self), self)) ever correct.

I think I've understood the point now thanks to this beatiful site and lovely community.

If you don't mind please correct me if I'm wrong on classmethods (which I am now trying to understand fully):


# EXAMPLE #1
>>> class A(object):
...     def foo(cls):
...             print cls
...     foo = classmethod(foo)
...
>>> a = A()
>>> a.foo()
# THIS IS THE CLASS ITSELF (__class__)
class '__main__.A'


# EXAMPLE #2
# SAME AS ABOVE (With new @decorator)
>>> class A(object):
...     @classmethod
...     def foo(cls):
...             print cls
...
>>> a = A()
>>> a.foo()
class '__main__.A'


# EXAMPLE #3
>>> class B(object):
...     def foo(self):
...             print self
...
>>> b = B()
>>> b.foo()
# THIS IS THE INSTANCE WITH ADDRESS (self)
__main__.B object at 0xb747a8ec
>>>

I hope this illustration shows ..

I've updated the article to make it a bit clearer: Python Attributes and Methods # Super

Your example using classmethod above shows what a class method is - it passes the class itself instead of the instance as the first parameter. But you don't even need an instance to call the method, for e.g.:

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

In Python 3, you can skip specifying arguments for 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."