关于python:在类方法中使用super

Using super with a class method

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

我以为我已经掌握了它,直到我通过这个例子(2.6)才发现自己卡住了。

http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html super with classmethod示例

1
2
3
4
5
6
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)
>>>

这不是我在示例前面看到这行时所期望的:

If we're using a class method, we don't have an instance to call super with. Fortunately for us, super works even with a type as the second argument. --- The type can be passed directly to super as shown below.

这正是Python告诉我的,不能通过说应该用B的实例调用do_Something()。


有时候,阅读文本更多的是为了想法的味道,而不是为了细节。这就是其中之一。

在链接页面中,示例2.5、2.6和2.7都应使用一种方法,即do_your_stuff。(即,do_something改为do_your_stuff)

此外,正如内德所指出的,A.do_your_stuff必须是一个类方法。

1
2
3
4
5
6
7
8
9
10
11
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作为方法do_you_stuff()的第一个参数被传递。

因此,super(B, cls).do_your_stuff()使Ado_your_stuff方法以cls调用,作为第一个参数传递。为了实现这一目标,Ado_your_stuff必须是类方法。链接页面没有提到,但这是真的。

ps.do_something = classmethod(do_something)是制作类方法的旧方法。新的(er)方法是使用@classmethod修饰符。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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之后的类。

1
2
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内部调用的,所以最终在无限循环中调用B.do_your_stuff

在python3中,增加了super的0变元形式,可以用super()代替super(B, cls),python3从上下文中可以看出class B定义中的super()应等同于super(B, cls)

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


在python 3中,可以跳过为super指定参数,

1
2
3
4
5
6
7
8
9
10
11
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."


我已经更新了这篇文章,使其更加清晰:python属性和方法super

上面使用class method的示例显示了类方法是什么——它将类本身而不是实例作为第一个参数传递。但您甚至不需要实例来调用该方法,例如:

1
2
3
4
5
6
7
>>> class A(object):
...     @classmethod
...     def foo(cls):
...         print cls
...
>>> A.foo() # note this is called directly on the class
<class '__main__.A'>

网页上的示例似乎与发布的一样有效。您是否也为超类创建了一个do_something方法,但没有将其变为类方法?这样的错误会出现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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)


我想我现在已经明白了这一点,多亏了这个美丽的地方和可爱的社区。

如果你不介意,请纠正我在类方法上的错误(我现在正试图完全理解):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 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
>>>

我希望这幅插图能说明……