English 中文(简体)
Using super with a class method
原标题:

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.

最佳回答

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.

问题回答

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

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 >

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)

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





相关问题
Can Django models use MySQL functions?

Is there a way to force Django models to pass a field to a MySQL function every time the model data is read or loaded? To clarify what I mean in SQL, I want the Django model to produce something like ...

An enterprise scheduler for python (like quartz)

I am looking for an enterprise tasks scheduler for python, like quartz is for Java. Requirements: Persistent: if the process restarts or the machine restarts, then all the jobs must stay there and ...

How to remove unique, then duplicate dictionaries in a list?

Given the following list that contains some duplicate and some unique dictionaries, what is the best method to remove unique dictionaries first, then reduce the duplicate dictionaries to single ...

What is suggested seed value to use with random.seed()?

Simple enough question: I m using python random module to generate random integers. I want to know what is the suggested value to use with the random.seed() function? Currently I am letting this ...

How can I make the PyDev editor selectively ignore errors?

I m using PyDev under Eclipse to write some Jython code. I ve got numerous instances where I need to do something like this: import com.work.project.component.client.Interface.ISubInterface as ...

How do I profile `paster serve` s startup time?

Python s paster serve app.ini is taking longer than I would like to be ready for the first request. I know how to profile requests with middleware, but how do I profile the initialization time? I ...

Pragmatically adding give-aways/freebies to an online store

Our business currently has an online store and recently we ve been offering free specials to our customers. Right now, we simply display the special and give the buyer a notice stating we will add the ...

Converting Dictionary to List? [duplicate]

I m trying to convert a Python dictionary into a Python list, in order to perform some calculations. #My dictionary dict = {} dict[ Capital ]="London" dict[ Food ]="Fish&Chips" dict[ 2012 ]="...

热门标签