Python

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

Jacob Zimmerman

Jacob is a certified Java programmer (level 1) and Python enthusiast. He loves to solve large problems with programming and considers himself pretty good at design.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button