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.
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 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;
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.
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.