Flagging classes as not intended for direct initialization

Chris Angelico rosuav at gmail.com
Mon Feb 16 09:11:01 EST 2015


On Tue, Feb 17, 2015 at 12:47 AM, Mario Figueiredo <marfig at gmail.com> wrote:
> The following is quoted from Learning Python, 5th Edition:
>
>>Java programmers may especially be interested to know that Python also has a super
>>built-in function that allows calling back to a superclass’s methods more generically—
>>but it’s cumbersome to use in 2.X; differs in form between 2.X and 3.X; relies on unusual
>>semantics in 3.X; works unevenly with Python’s operator overloading; and does not
>>always mesh well with traditionally coded multiple inheritance, where a single superclass call won’t suffice.
>>
>>In its defense, the  supercall has a valid use case too—cooperative same-named method
>>dispatch in multiple inheritance trees—but it relies on the “MRO” ordering of classes,
>>which many find esoteric and artificial; unrealistically assumes universal deployment
>>to be used reliably; does not fully support method replacement and varying argument
>>lists; and to many observers seems an obscure solution to a use case that is rare in real
>>Python code.
>>

Let's take a step back here.

super() depends on the "MRO" ordering of classes. Yes, but that's not
super()'s decision - that is, quite fundamentally, how Python classes
work. Here's a quick look at the MRO; this should work equally in Py2
and Py3:

class Top(object):
    def foo(self):
        print("I am top - foo")
    def top(self):
        print("I am top - top")

class Left(Top):
    def foo(self):
        print("I am left - foo")
        super(Left, self).foo()
    def leftright(self):
        print("I am left - leftright")

class Right(Top):
    def foo(self):
        print("I am right - foo")
        super(Right, self).foo()
    def leftright(self):
        print("I am right - leftright")

class Diamond1(Left, Right):
    def foo(self):
        print("I am diamond1 - foo")
        super(Diamond1, self).foo()
    # No leftright method

class Diamond2(Left, Right):
    def foo(self):
        print("I am diamond2 - foo")
        super(Diamond2, self).foo()
    def leftright(self):
        print("I am diamond2 - leftright")
        super(Diamond2, self).leftright()

print(Diamond1.__mro__)
print(Diamond2.__mro__)
d1 = Diamond1()
d2 = Diamond2()
d1.foo()
d2.foo()
d1.leftright()
d2.leftright()

Note that calling d1.leftright() calls the method from Left, not the
one from Right; and calling super() from inside Diamond2's leftright
does the exact same thing.

So what you have here is not "super() is weird", but "multiple
inheritance is messy, and this is how Python handles it".

I agree with the "cumbersome to use in 2.x", but it's not actually
different in syntax across the two - correct me if I'm wrong, but I
believe that if you write it out in full, your code is guaranteed to
work equally in both branches. So you can't complain about the
difference AND complain about the old syntax being clunky; the
difference is precisely the solution to the clunkiness.

"unrealistically assumes universal deployment to be used reliably".
Hmm. We're talking here about a solution to diamond inheritance. Can
you name *any* solution to that problem which does not require some
careful management by all involved classes? In any language at all?

Multiple inheritance is the issue. If you want to defer anything till
a later chapter, defer that. Once you decide to learn about MI, you
just have to knuckle down and learn Python's particular flavour of MI,
because it's different from C++'s (both default and virtual
inheritance), different from Java's ("there is no MI"), different from
Pike's, etc. And when you learn Python's MI, what you'll be learning
is the MRO; super() just comes along for the ride.

Explicit naming of superclasses is fragile for several reasons.
Firstly, it breaks the parallel between "method which calls its
superclass method" and "absence of method, and implicit referral to
superclass" (as the latter guarantees to use the MRO); and secondly,
you end up writing your superclass name a lot of times. Though, to be
honest, the Py2 super() syntax has the same issue, just with your own
class name. With the Py3 shorthand syntax, you're not rewriting
everything all over the place.

ChrisA



More information about the Python-list mailing list