Python Inheritance is More Powerful (and More Dangerous) Than Most
This is going to be a short one to get me back into writing again. I just wanted to point out some interesting things I’ve noticed about Python inheritance that I’m (sometimes) sad that Java or C# don’t do.
Multiple Inheritance
The most obvious thing that Python has that the others don’t is multiple inheritance, and the way that multiple inheritance is handled is also quite elegant and powerful(see Super() Considered Super – the article or the video).
This is awesome, but as seems to often be the case, this power comes at the price of danger. If one doesn’t design the parameter list and super()
calls correctly for inheritance, especially with __init__()
, it can be easy to accidentally have an incompatible class get injected into the MRO. Let’s have an example:
class Base: def __init__(self, a): self.a = a class One(Base): def __init__(self, a, b): super().__init__(a) self.b = b class Half(Base): def __init__(self, a, c): super().__init__(a) self.c = c class Multi(One, Half): def __init__(self, a, b, c): # there is no super() call that can work here.
With the creation of Multi
, the MRO becomes Multi
, One
, Half
, Base
. Multi
can’t make any super()
calls to __init__()
that will work. These types of things need to be designed to be inherited from (which is outside the topic of this article; maybe soon), or else you could easily end up in a mess like this. If you’re trying to combine two classes that you have no control over, you’ll need to somehow look at their source code to see if they’re designed for dealing with this or not.
This is the reason why most advice given for using multiple inheritance says that only the first class in an inheritance list should be a proper, full class, and the other classes listed should be mixins or other “partial” classes. I’ll also add that if you can use delegation instead of inheritance, you probably should. Both tips help to avoid problems such as these
Inheritance of Class-Level Attributes
Classes in Python don’t just inherit the instance API (the fields and regular methods) the way they do in other languages; classmethod
s, staticmethods
, and other class-level attributes are actually inherited by subclasses as well.
Again, this can be pretty helpful, especially since a classes are objects that can be passed around like anything else (Class
reflection classes in other languages aren’t the same thing). But, again, it has the potential to be dangerous.
Say, for example you have a classmethod
that is used as factory method for creating an instance of the class it resides on. Well, you can call that from a subclass under the assumption that it will create an instance of the subclass, but it won’t work if the subclass requires additional arguments in its constructor.
Outro
So, as you can see, Python’s inheritance abilities are notably more powerful than a lot of mainstream languages’ inheritance, but those abilities come at a price of being potentially dangerous.
I notice these types of trade-offs a lot in language design, and it’s certainly not a flaw in Python; it’s simply something to be aware of.
Reference: | Python Inheritance is More Powerful (and More Dangerous) Than Most from our WCG partner Jacob Zimmerman at the Programming Ideas With Jake blog. |