将 super 与类方法一起使用

发布于 2024-08-13 16:33:47 字数 946 浏览 7 评论 0原文

我正在尝试学习 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 告诉我,应该用 B 的实例调用 do_something() ,这正是不可能的。

I'm trying to learn the super() function in Python.

I thought I had a grasp of it until I came over this example (2.6) and found myself stuck.

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

It wasn't what I expected when I read this line right before the example:

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.

Which is exactly what Python tells me is not possible by saying that do_something() should be called with an instance of B.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小…楫夜泊 2024-08-20 16:33:47

有时,必须更多地阅读文本才能了解想法的风格,而不是细节。这是其中之一。

链接页面中,示例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 进行调用。为了使其发挥作用,A
do_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:调用 Python 对象时超出最大递归深度

如果 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 内部调用的,因此您最终会在无限循环中调用 B.do_your_stuff

在 Python3 中,0 参数形式的 super 是为了让 super(B, cls) 可以被 super() 替换,并且 Python3 会从上下文中找出 super() class B 定义中的 > 应该等同于 super(B, cls)

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

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.

笑忘罢 2024-08-20 16:33:47

在 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."

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."
内心激荡 2024-08-20 16:33:47

我更新了这篇文章,使其更加清晰: Python属性和方法 # Super

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

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

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'>
唱一曲作罢 2024-08-20 16:33:47

网页上的示例似乎可以按发布的方式工作。您是否也为超类创建了一个 do_something 方法,但没有将其放入类方法中?像这样的事情会给你这个错误:

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

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)
马蹄踏│碎落叶 2024-08-20 16:33:47

我想我现在已经明白了这一点,感谢这个美丽的网站和可爱的社区。

如果您不介意,如果我在类方法上错了(我现在正在尝试完全理解),请纠正我:


# 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 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 ..

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文