[Python-ideas] Is multiple inheritance Pythonic?

Andrew Barnert abarnert at yahoo.com
Fri Mar 18 19:50:49 EDT 2016


On Mar 18, 2016, at 15:34, Martin Teichmann <lkb.teichmann at gmail.com> wrote:
> 
> Hi,
> 
>>> Is multiple inheritance bad, or not Pythonic?
>> 
>> Definitely not.
>> 
>> But at the same time, it's not always the answer to everything
>> either.
> 
> Actually, I agree with you. But then I found the discussion about
> asyncio.BaseEventLoop.create_task (especially:
> https://github.com/python/asyncio/issues/208   and
> https://codereview.appspot.com/110820049/  ),
> which is now in the standard library, probably the most Pythonic
> code out there.

What makes asyncio the most Pythonic code out there, as opposed to other stdlib modules like enum, selectors, etc.?[1]

> During this discussion, all participants argue as if class inheritance
> was something EVIL.

You're misinterpreting them. They believe that making people inherit from API classes and override internal methods is not the best way to allow customizing those APIs. That one particular use of inheritance is almost always a bad one. 

But that doesn't at all mean that all possible uses of inheritance are bad. For example, nobody suggested that it was evil for IntEnum to subclass both int and Enum--in fact, Enum was specifically designed (and debugged) to make that as easy as possible, and to allow IntEnum to serve as an example for user enum types. And a lot of the same people were involved in that discussion.

So, what _are_ the reasons inheritance isn't the best way to handle this kind of customization? The two most famous are:

1. With subclassing-for-specialization, it's rarely clear--even in a language like C++ where the required protected and virtual act as markers, much less in Python--which methods you are expected to override.[2] With a set_spam_cheeser interface, it's obvious what they expected you to customize: just the spam-cheeser.

2. Who constructs the object? Often it's the framework itself, which means if the only way for you to customize framework.MeatCooker.spam_cheeser is to subclass, then you also need some way to tell framework.Kitchen to create an app.MyMeatCooker instead of a framework.MeatCooker. Which means you need a framework.KitchenFactory that you can tell to create an app.MyKitchen... Other times, the app creates the object, synthesizing customizations from various different third-party libs, but inheritance can cause its own problems there.[4]

The general trend here is easier to see (and therefore so are the motivations behind it) in the brace-family languages. Unlike Python, which has always[5] treated classes and methods and everything else as first-class objects, they have to invent a whole new language feature (first interfaces, then delegates/method-pointers, then event-handlers) at each step, which usually means a whole new language and stdlib. Fortunately, when people figure out a better way of doing things in Python, we can just gradually start using the better way in place of the old way.

---


[1]: In fact, I suspect Guido would say that there are many ways in which asyncio is a special case, which is part of the reason he had to write a relatively complex stdlib module in the first place (rather than the usual pattern of letting different alternatives compete and then maybe considering adding a simplified version of the winner to the stdlib years later).

[2]: And often, if you override methods the class designer didn't expect, it works in some cases but not others, so you design around it and then realize it doesn't work and isn't supposed to work, and then you end up having to override three other methods and copy and paste all but one line and then subclass an associated class and override two of its methods to fix the bug. 

[3]: ... which may be an interface with multiple methods, but most simply it's just one function.

[4]: Admittedly, those problems aren't as bad in Python as in brace-family language--it just means you have to define and document a way for all those libraries and apps to cooperate properly, and write your code carefully so that bad cooperation can be debugged, which isn't all that hard. But it's still a lot harder than not doing it, and you don't have to do any of that with separate customization hooks.

[5]: OK, there were some limitations before 2.2, but I think we can ignore those.


More information about the Python-ideas mailing list