Question

This works as expected:

>>> class Foo(object):
...   @classmethod
...   def hello(cls):
...     print 'hello, foo'
... 
>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print 'hello, bar'
...     super(Bar, cls).hello()
... 
>>> b = Bar()
>>> b.hello()
hello, bar
hello, foo

I can also call the base class explicitly:

>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print 'hello, bar'
...     Foo.hello()
... 
>>> b = Bar()
>>> b.hello()
hello, bar
hello, foo

I was wondering why I can't omit the first argument to super, like this:

>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print 'hello, bar'
...     super(Bar).hello()
... 
>>> b = Bar()
>>> b.hello()
hello, bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in hello
AttributeError: 'super' object has no attribute 'hello'

when the result of the super call without a second argument seems to be a class type inside a super type:

>>> class Bar(Foo):
...   @classmethod
...   def hello(cls):
...     print Foo, type(Foo)
...     print super(Bar), type(super(Bar))
...     print cls, type(cls)
... 
>>> b = Bar()
>>> b.hello()
<class '__main__.Foo'> <type 'type'>
<super: <class 'Bar'>, NULL> <type 'super'>
<class '__main__.Bar'> <type 'type'>

I guess I'm just wondering about the design here. Why would I need to pass the class object into the super call to get a reference to the base class type Foo? For a normal method, it makes sense to pass self to the function, since it needs to bind the base class type to an actual instance of the class. But a classmethod doesn't need a specific instance of the class.

EDIT: I get the same error in Python 3.2 as I do above in 2.7 for super(Bar).hello(). However, I can simply do super().hello() and that works fine.

Was it helpful?

Solution

super() returns a descriptor, and needs two items:

  • A starting point from which to search the class hierarchy.
  • The argument to bind the returned methods.

For the two argument (and implicit zero-argument *) case the second argument is used to bind to, but if you do not pass in a second argument, super() cannot invoke the descriptor protocol to bind the returned functions, classmethods, properties or other descriptors. classmethods are still descriptors and are bound; the bind to a class and not an instance, but super() does not know how the descriptor will use the context to which you bind.

super() should not and cannot know that you are looking up a class method instead of a regular method; class methods only differ from regular methods because their .__get__() method acts differently.

Why are class methods bound? Because when you subclass Foo but do not override .hello(), calling Bar.hello() invokes the Foo.__dict__['hello'] function, binds it to Bar and your first argument to hello(cls) will be that subclass, not Foo.

Without a second argument, super() returns an unbound object that can manually be bound later on. You can do the binding yourself using the .__get__() method provided by the super() instance:

class Bar(Foo):
    @classmethod
    def hello(cls):
        print 'hello, bar'
        super(Bar).__get__(cls, None).hello()

super().__get__() on an instance without a context effectively returns a new super() instance with the context set. On an instance with a context .__get__() just returns self; it is already bound.


* In Python 3, calling super() without arguments from inside a bound method will use the calling frame to discover, implicitly, what the type and bound object are, so you no longer have to explicitly pass in the type and object arguments in that case. Python 3 actually adds a implicit __class__ closure variable to methods for this purpose. See PEP 3135 and Why is Python 3.x's super() magic?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top