[Python-ideas] Composition over Inheritance

Soni L. fakedme+py at gmail.com
Sat Oct 28 08:19:09 EDT 2017

On 2017-10-28 09:51 AM, Steven D'Aprano wrote:
> On Sat, Oct 28, 2017 at 09:09:30AM -0200, Soni L. wrote:
>> As recent threads indicate, composition may sometimes be better than
>> inheritance. And so I'd like to propose composition as a built-in feature.
>> My idea is syntax of the form o.[c].m(), where o is an object, c is a
>> component, m is a method.
> How is that different from o.c.m() which works today?
> My understanding of composition is simply setting an attribute of your
> instance to the component, then calling methods on the attribute. How
> does that differ from what you are describing?
> Instead of the classic multiple-inheritence:
> class Car(Engine, AcceleratorPedal, GasTank, ...):
>      pass
> which requires each superclass to be designed to work with each other
> (e.g. you can't have both EntertainmentSystem.start() and
> Ignition.start(), unless you want the ignition to automatically turn on
> when the entertainment system does)
> we can instead use composition and delegation:
> class Car:
>      def __init__(self):
>          self.engine = Engine()
>          self.accelerator = AcceleratorPedal()
>          ...
>      def start(self):
>          # Delegate to the ignition component.
>          self.ignition.start()
> etc. Obviously this is just a very loose sketch, don't take it too
> literally. Is this the sort of thing you are talking about?

So how do you call car.ignition.start() from car.key.turn()?

>> I am not sure how you'd set components, or test for components,
> If you don't know how to set components, or test for them, what do you
> know how to do with components?
> And how are components different from attributes?

They're more like conflict-free interfaces, and in this specific case 
they're designed with duck typing in mind. (You can dynamically add and 
remove components, and use whatever you want as the component. You 
cannot do that with inheritance.)

>> but I don't think it makes sense to be able to do o.[c][x] or x=o.[c], because
>> those break the idea of automatically passing the object as an argument
>> (unless we create whole wrapper objects every time the syntax is used,
>> and that's a bit ew. also how would you handle o.[c1].[c2] ?).
> I'm afraid I do not understand what you are talking about here.
> If might help if you give a concrete example, with meaningful names. It
> would help even better if you can contrast the way we do composition now
> with the way you think we should do it.
> I'm afraid that at the moment I'm parsing your post as:
> "composition is cool, we should use it; and o.[c].m() is cool syntax, we
> should use it for composition; I'll leave the details to others".

Again, how do you call car.ignition.start() from car.key.turn()?

>> Thus I think o.[c].m() should be syntax sugar for o[c].m(o), with o
>> being evaluated only once,
> I don't see why you're using __getitem__ instead of attribute access;
> nor do I understand why m gets o as argument instead of c.
> Wait... is this something to do with Lieberman-style delegation?
> http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.html
> http://code.activestate.com/recipes/519639-true-lieberman-style-delegation-in-python/

TL;DR. But no, it's not some form of delegation.

It still gets `self` (which is whatever is in o[c] - which may be c 
itself, or an arbitrary object that fulfills the contract defined by c), 
but also gets `o` in addition to `self`. (Unless it's a plain function, 
in which case it gets no `self`.)

>> as that solves a lot of current issues
>> relating to inheritance while introducing very few issues relating to
>> python's "everything is separate" (e.g. __get__ vs __getattr__)
>> policy.This also makes setting components and testing for components
>> fairly trivial, and completely avoids the issues mentioned above by
>> making their syntax illegal.
> Above you said that you don't know how to set and test for components,
> now you say that doing so is trivial. Which is it?

If you pay closer attention, you'll notice the two different paragraphs 
talk about two different syntaxes.

- o.[c] as a standalone syntax element, allowing things like 
x=o.[c1].[c2]; and x=o.[c1][c2];.
- o.[c].m() as a standalone syntax element, *disallowing* the above.

>> (Disclaimer: This was inspired by my own programming language,
>> Cratera[1], so I'm a bit biased here. Cratera was, in turn, inspired by
>> Rust[2] traits.
> Traits are normally considered to be a more restricted, safer form of
> multiple inheritence, similar to mixins but even more restrictive.

What do you mean more restricted? They let you have the same method in 
multiple components/traits and not have them conflict, among other 
things. My variant also makes them dynamic and ducky, making them even 
more relaxed. Definitely (still) safer tho.

> http://www.artima.com/weblogs/viewpost.jsp?thread=246488
>> [1] https://bitbucket.org/TeamSoni/cratera
>> [2] https://www.rust-lang.org/

More information about the Python-ideas mailing list