Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 963
dza Avatar asked Nov 29 '09 23:11

dza


People also ask

How do you call the super class method in subclass?

Subclass methods can call superclass methods if both methods have the same name. From the subclass, reference the method name and superclass name with the @ symbol.

Can you call super in Classmethod?

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.

What is super () __ init __?

The “__init__” is a reserved method in python classes. It is known as a constructor in Object-Oriented terminology. This method when called, allows the class to initialize the attributes of the class. Python super() The super() function allows us to avoid using the base class name explicitly.


2 Answers

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.

like image 174
unutbu Avatar answered Oct 12 '22 11:10

unutbu


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." 
like image 41
Tankobot Avatar answered Oct 12 '22 11:10

Tankobot