This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author steven.daprano
Recipients docs@python, jdemeyer, rhettinger, steven.daprano
Date 2019-06-07.11:34:28
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <20190607113422.GL4221@ando.pearwood.info>
In-reply-to <1559901431.74.0.0354897681455.issue37176@roundup.psfhosted.org>
Content
On Fri, Jun 07, 2019 at 09:57:11AM +0000, Jeroen Demeyer wrote:

> I'm having problems with the first word of "a parent or sibling class of type". 

The first word is "a". Did you mean something else or did you mean it 
literally?

If the second case, we have to talk about *a* parent class rather than 
*the* parent class, since Python supports multiple inheritance and a 
class can have more than one parent class.

> The most important part of super() is to *which* class that attribute 
> lookups are delegated to and this is not explained.

Lookups are delegated to a parent or sibling class, exactly as the 
documentation says. Which class that is (whether a parent or sibling) 
can only be determined at runtime.

> And the sentence "The __mro__ attribute of the type lists the method 
> resolution search order used by both getattr() and super()" is even 
> wrong or at least confusing: what matters is not the MRO of the type 
> (the first argument to super()) but the MRO of ***the type of*** the 
> object (the second argument to super()).

I believe that is still wrong.

What matters is the __mro__ attribute of the first argument. It matters 
because that is how the MRO actually is searched.

The MRO of the second argument is not relevant, except to the degree 
that it overlaps with the __mro__ attribute of the first. And it will 
always overlaps, because the second argument must be an instance or 
subclass of the first. But not necessarily *directly* of the first:

object > Parent > A > B > C > D > E > F > instance = F()

If the method is defined in class C, then the super call in C should be 
written:

    class C(B):
        def method(self, arg):
            super().method(arg)  # like super(C, self).method(arg)

and the lookup will start at C.__mro__[1], namely B, precisely as 
expected. If it started back at the type of the instance (self), namely 
class F, the C.method would recursively call itself over and over and 
over again.

If you don't believe me, you can simulate super() working the way you 
suggest by doing this:

# -----%<-----

class A(object):
    def method(self):
        print("from class A")

class B(A):
    def method(self):
        print("bad method")
        # This is bad, don't do it!
        super(type(self), self).method()

class C(B): pass

C().method()

# -----%<-----

For multiple inheritance:

    class C(B, X, Y, Z): ...

we can't generally tell in advance which will be looked at next, since 
that depends on the precise details of the inheritance diagram. But 
whatever the class is, at this point, it is still the __mro__ of C that 
is used, not the MRO of the instance.

The bottom line: what matters is <first argument>.__mro__, exactly as 
documented.
History
Date User Action Args
2019-06-07 11:34:29steven.dapranosetrecipients: + steven.daprano, rhettinger, docs@python, jdemeyer
2019-06-07 11:34:29steven.dapranolinkissue37176 messages
2019-06-07 11:34:28steven.dapranocreate