Method resolution for super(Class, obj).

D

ddtl

Hello everybody.

Consider the following code:


class A(object):
def met(self):
print 'A.met'
class B(A):
def met(self):
print 'B.met'
super(B,self).met()
class C(A):
def met(self):
print 'C.met'
super(C,self).met()
class D(B,C):
def met(self):
print 'D.met'
super(D,self).met()
d = D()
d.met()


When executed, it prints:

D.met
B.met
C.met
A.met

The book (Python in a nutshell, 2nd edition) explains:

"The solution is to use built-in type super. super(aclass, obj),
which returns a special superobject of object obj. When we look
up an attribute (e.g., a method) in this superobject, the lookup
begins after class aclass in obj's MRO."

But I don't understand - MRO means that when attribute is found
somewhere in hierarchy, the search for it stops, that is: when
d.met() is executed, it is supposed to print 'D met', call
super(D,self).met() which should resolve met() to be B's attribute,
and after B's met() is executed, we should be done. Why does the
lookup proceeds from B to C as though met() wasn't found in B?
Indeed, lookup order (according to a new-style MRO) is B, then C
and at last A (because of a diamond inheritance), but only when
attribute is not found in B it is looked up in C, and only if it
is not found neither in B nor in C it is looked up in A...

What is different here?

ddtl.
 
J

Jason

ddtl said:
Hello everybody.

Consider the following code:


class A(object):
def met(self):
print 'A.met'
class B(A):
def met(self):
print 'B.met'
super(B,self).met()
class C(A):
def met(self):
print 'C.met'
super(C,self).met()
class D(B,C):
def met(self):
print 'D.met'
super(D,self).met()
d = D()
d.met()


When executed, it prints:

D.met
B.met
C.met
A.met

The book (Python in a nutshell, 2nd edition) explains:

"The solution is to use built-in type super. super(aclass, obj),
which returns a special superobject of object obj. When we look
up an attribute (e.g., a method) in this superobject, the lookup
begins after class aclass in obj's MRO."

But I don't understand - MRO means that when attribute is found
somewhere in hierarchy, the search for it stops, that is: when
d.met() is executed, it is supposed to print 'D met', call
super(D,self).met() which should resolve met() to be B's attribute,
and after B's met() is executed, we should be done. Why does the
lookup proceeds from B to C as though met() wasn't found in B?
Indeed, lookup order (according to a new-style MRO) is B, then C
and at last A (because of a diamond inheritance), but only when
attribute is not found in B it is looked up in C, and only if it
is not found neither in B nor in C it is looked up in A...

What is different here?

Let's examine what the mro order is for class D:[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__mai
n__.A'>, <type 'object'>]

When you call d.met(), the call dispatches to the D.met() method.
After printing out 'D.met', you use super() to get the next class in
the mro order, and call that class's met method.

As shown with the mro(), the class after D is B. So B.met() is called.
Normally, we would be done. But take a look at B's method!
class B(A):
def met(self):
print 'B.met'
super(B,self).met()

B.met calls super, and invokes the next met method! So, the code does
exactly what you've asked it to do, and searches for the next class
after B in the mro list: class C. You are then invoking met method of
that class. So, class B is calling class C's met method.

Class C also uses super, and calls the resulting met method on the
result as well. This finds class A as the next class in the mro list,
and invokes the met method on it as well.

When you get to A's met method, you aren't calling another met method,
so the print statements end.

If you want the dispatch to end at B's method, comment out the
'super(B,self).met()' line:.... def met(self):
.... print 'B2.met'

Alternatively, you could do away with using super entirely, and
actively call the superclass method that you want:.... def met(self):
.... print 'D2.met'
.... B2.met(self) # Invoke B2's method directly
....D2.met
B2.met

You don't need super() to call a superclass method. It can help with
complex class heirarchies, but most single-descendent class structures
don't need it. Either way, when designing a class heirarchy, you
should either always use super() or never use super(). Mixing
non-super-using and super-using can give you problems.

(Rhetorical Q: Does this make me more or less super?)

--Jason
 
S

Steve Holden

ddtl said:
Let's examine what the mro order is for class D:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__mai
n__.A'>, <type 'object'>]

When you call d.met(), the call dispatches to the D.met() method.
After printing out 'D.met', you use super() to get the next class in
the mro order, and call that class's met method.

As shown with the mro(), the class after D is B. So B.met() is called.
Normally, we would be done. But take a look at B's method!

class B(A):
def met(self):
print 'B.met'
super(B,self).met()

B.met calls super, and invokes the next met method! So, the code does
exactly what you've asked it to do, and searches for the next class
after B in the mro list: class C.


But when super(B,self).met() is invoked, isn't it supposed to look
at MRO order of *B*, which is:

(<class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
No, that's the mistake people often make. An instance of type B would
see B's MRO, but an instance of type D sees D's MRO.
and B doesn't have any relation with C, that is: A's met() is the to
be called as a result. In effect, what you say impies that a call to
super() is context dependant - if super(B,self).met() is invoked as
a result of a call to D().met(), the effect is different from the effect
of a call to B().met(). But a documentation of a super() doesn't mention
anything like that (or at least I didn't find it), and it seems a
significant piece of information. Doesn't it imply that there should
be another explanation?

It's all rather better explained in the Nutshell Guide than it is in the
Python documentation. But basically you've got it right: it's the class
of the *instance* that determines the MRO used by super().

regards
Steve
 
D

ddtl

Let's examine what the mro order is for class D:[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__mai
n__.A'>, <type 'object'>]

When you call d.met(), the call dispatches to the D.met() method.
After printing out 'D.met', you use super() to get the next class in
the mro order, and call that class's met method.

As shown with the mro(), the class after D is B. So B.met() is called.
Normally, we would be done. But take a look at B's method!
class B(A):
def met(self):
print 'B.met'
super(B,self).met()

B.met calls super, and invokes the next met method! So, the code does
exactly what you've asked it to do, and searches for the next class
after B in the mro list: class C.

But when super(B,self).met() is invoked, isn't it supposed to look
at MRO order of *B*, which is:

(<class '__main__.B'>, <class '__main__.A'>, <type 'object'>)

and B doesn't have any relation with C, that is: A's met() is the to
be called as a result. In effect, what you say impies that a call to
super() is context dependant - if super(B,self).met() is invoked as
a result of a call to D().met(), the effect is different from the effect
of a call to B().met(). But a documentation of a super() doesn't mention
anything like that (or at least I didn't find it), and it seems a
significant piece of information. Doesn't it imply that there should
be another explanation?

ddtl.
 
B

Bruno Desthuilliers

ddtl a écrit :
Let's examine what the mro order is for class D:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__mai
n__.A'>, <type 'object'>]

When you call d.met(), the call dispatches to the D.met() method.
After printing out 'D.met', you use super() to get the next class in
the mro order, and call that class's met method.

As shown with the mro(), the class after D is B. So B.met() is called.
Normally, we would be done. But take a look at B's method!

class B(A):
def met(self):
print 'B.met'
super(B,self).met()

B.met calls super, and invokes the next met method! So, the code does
exactly what you've asked it to do, and searches for the next class
after B in the mro list: class C.


But when super(B,self).met() is invoked, isn't it supposed to look
at MRO order of *B*,

No. It's supposed to look at the MRO of self for what comes after B.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,732
Latest member
ArronPalin

Latest Threads

Top