[Python-3000] auto-super()
Thomas Wouters
thomas at python.org
Wed Apr 19 11:52:27 CEST 2006
On 4/19/06, Greg Ewing <greg.ewing at canterbury.ac.nz> wrote:
>
> Thomas Wouters wrote:
>
> > The arguments against super() are
> > understandable -- if you only think about your own class, or completely
> > disregard MI. I think that's irresponsible: super() always calls the
> > right baseclass method,
>
> On the contrary, it's precisely when you *don't* know
> what classes are going be mixed in with you that super()
> is likely to go haywire.
No, really, on the contrary, it's precisely when you don't know what classes
are going to be mixed in with you that super() is what you need to use. If
you do not use super(), you *will* be calling the wrong method. If you do
use super(), you will be calling the right method, but you *may* pass the
wrong arguments. The set of failure cases is very simply just smaller for
super()-using classes.
Your super() call ends up
> calling a method that you know nothing about,
This is violence inherent in the system. This is *why* you're calling the
baseclass method. You don't want to figure out the work yourself, you want
to ask your baseclass "do your thing as if I weren't there and you got these
arguments". The only assumption you make is that the method signature is
compatible, and that is indeed unfortunate (but unavoidable, in the current
state of affairs. Exclusively using keyword arguments helps a bit, though.)
and the method being called isn't expecting to be called
> in that context either.
A method that isn't expecting to be called by subclasses sounds like a
broken method.
Whether that will do the
> "right" thing, or whether there even is a right thing
> to be done, is anybody's guess.
If two methods of the same name on different classes do wildly different
things, your class hierarchy is broken, and it's completely unrelated to
using super() or not. If you want the 'left' part of the diamond to take
care of method 'spam' even though the 'right' part of the diamond also
defines 'spam', not using super() will do what you want -- except it's
horribly fragile. As soon as the right part of the diamond calls spam (and
expects the right-handed spam to be called, which it can't help but do)
things blow up; it will get the left-handed spam instead. That situations in
unfixably broken. Using super() just makes the break more apparent (and,
apparently, gives you an easy target to blame :-)
If you have two methods of the same name that do the same thing, and expect
to be called in the same situations, but have incompatible function
signatures, it is also unfixably broken. When you use super(), the left side
ends up calling the right-handed method as if it was the baseclass method,
possibly passing too few arguments or the wrong ones, or not passing a bit
of extra information the right-side could have used. When you don't use
super(), you end up ignoring the right hand side entirely, quite likely
breaking the entire functionality of that part of the inheritance tree.
And if you have two methods that don't have a signature incompatible from
the baseclass signature (meaning any extra arguments are optional), super()
does the right thing but explicit baseclass calls do not. It's as simple as
that.
> What causes trouble isn't multiple inheritance, it's
> diamond inheritance.
Sorry, that's wishful thinking. Multiply-inheriting classes in Py3K are
always involved in diamond inheritance, because they are always what we now
call "new-style". And it's not an insignificant diamond, 'object' provides
methods such as __getattribute__ that are really necessary, and really used.
Without diamond inheritance,
> explicit calls to inherited methods usually work fine,
> and continue to work fine when your classes get
> inherited, multiply or otherwise, by other people's
> classes, as long as they do something sensible with
> their inherited calls.
No, as long as there is *no* conflict between the methods of the two sides.
Assuming that is, again, wishful thinking, since you have no control over
what subclasses do. And if they 'do something sensible', super() *also* does
the right thing -- it just does the right thing in more cases to boot.
> Personally I think that the use of diamond inheritance
> should be severely discouraged, if not banned completely.
> There's a fundamental problem with it: different
> subclasses can have different ideas on how the diamond-
> inherited class should be initialised, and otherwise
> used. That seems to me an inherent flaw in the notion
> of diamond inheritance, and not something that super()
> or anything else can fix.
As Guido pointed out, diamond inheritance isn't fundamentally flawed, it
just requires a level of co-operation we aren't explicitly requiring, in
Python. Fortunately, Python is quite flexible, and we can add that
requirement in a custom metaclass. It would walk the namespace of any newly
created class, recording method signatures, and comparing them against
signatures of baseclasses. Or we could use decorators to indicate willing
co-operation and warn against methods of the same name that don't say they
play well with others. I've thought about using a metaclass like that, but I
end up not needing it enough. Likewise, if you insist on not using MI (or
super()), a metaclass could easily be made to warn against uses of MI -- and
I did make that one for someone, once:
import warnings
class DontMIType(type):
def __init__(self, name, bases, dict):
if bases and len(bases) > 1:
# Find the first baseclass to be of our type
firstbase = self
for base in bases:
if not isinstance(base, DontMIType):
break
firstbase = base
warnings.warn("Class %s multiply inherits, but baseclass %s "
"disallows multiply inheriting" % (self,
firstbase))
return super(DontMIType, self).__init__(name, bases, dict)
It's only avisory, because you can easily circumvent this metaclass in
subclasses by subclassing the metaclass and not calling the base-metaclass
__init__, then using the sub-metaclass as metaclass for subclasses of
whatever class doesn't like participating in MI. Besides, mandating such
things isn't very Pythonic. It's a clear enough signal that the gun is now
pointed at your wriggly little toes.
Whenever I've tried to use diamond inheritance, I've
> always ended up regretting it, and re-designing my class
> hierarchy to eliminate it. Once I've done that, any
> reason I had to use super() invariably disappears.
If you have that luxury, that's fine. I'd encourage anyone to avoid MI (and
use the above metaclass to avoid stepping into unexpected puddles later in
life, of course.) I tend to use metaclasses instead of MI myself -- except
*in* metaclass hierarchies, but that has a practical reason: Python *does*
require metaclass compatibility. Metaclasses make for a good example on how
(and why) to use super(), because the methods you use on metaclasses (by and
large) are the __init__, __new__, __getattribute__ and __setattribute__
methods, and changing their signature is folly.
But there are actual use-cases for MI in 'normal' classes, too, and Python
still has to try and cater to them as best it (and we) can. And super() is
part of that.
--
Thomas Wouters <thomas at python.org>
Hi! I'm a .signature virus! copy me into your .signature file to help me
spread!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.python.org/pipermail/python-3000/attachments/20060419/71b7fc9d/attachment-0001.htm
More information about the Python-3000
mailing list