Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a classmethod's super need a second argument?

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.

like image 587
jterrace Avatar asked Jul 10 '13 15:07

jterrace


People also ask

Is it possible to call Super () without the props argument?

But somehow, even if you call super () without the props argument, you’ll still be able to access this.props in the render and other methods. (If you don’t believe me, try it yourself!) How does that work? It turns out that React also assigns props on the instance right after calling your constructor:

What is a super class in JavaScript?

In JavaScript, super refers to the parent class constructor. (In our example, it points to the React.Component implementation.) Importantly, you can’t use this in a constructor until after you’ve called the parent constructor.

Is it necessary to always pass Super to the constructor?

But this.props would still be undefined between the super call and the end of your constructor: It can be even more challenging to debug if this happens in some method that’s called from the constructor. And that’s why I recommend always passing down super (props), even though it isn’t strictly necessary:

Can you just write Super () instead of Super (props) in react?

So React was intentionally unopinionated about whether calling super () is required — even though ES6 classes are. So does this mean you can just write super () instead of super (props)? Probably not because it’s still confusing. Sure, React would later assign this.props after your constructor has run.


1 Answers

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?

like image 150
Martijn Pieters Avatar answered Oct 22 '22 23:10

Martijn Pieters