[Python-ideas] Composition over Inheritance

Thomas Jollans tjol at tjol.eu
Sat Oct 28 08:50:43 EDT 2017

On 28/10/17 14:19, Soni L. wrote:
> 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()?

self.car.ignition.start() of course.

If the key has to do something involving the car, it has to know about
the car, so tell it about the car:

class Car:
    def __init__(self):
        self.engine = Engine()
        self.accelerator = AcceleratorPedal(self.engine)
        self.ignition = Ignition(self)
        self.key = Key(self)
        # and so on.

FWIW I haven't the faintest idea what you're talking about. Please
provide an example that shows how you might create a "component" and use
it. Ideally, comparing it with an example of how you would currently so
the same thing in Python.

>>> 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/
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/

More information about the Python-ideas mailing list