Re: [Python-ideas] Composition over Inheritance

2017-10-30 Thread Thomas Jollans
On 29/10/17 19:25, Soni L. wrote:
> 
> 
> On 2017-10-29 02:57 PM, Brendan Barnwell wrote:
>> On 2017-10-29 04:44, Soni L. wrote:
>>> And this is how you miss the whole point of being able to dynamically
>>> add/remove arbitrary components on objects you didn't create, at
>>> runtime.
>>>
>>> Someone gave me this code and told me it explains what I'm trying to do:
>>> https://repl.it/NYCF/3
>>>
>>> class T:
>>>  pass
>>>
>>> class C:
>>>  pass
>>>
>>> c = C()
>>>
>>> #c.[T] = 1
>>> c.__dict__[T] = 1
>>
>> Again, can you please explain why you want to write c.[T]? What do
>> you intend that to *do*?  Your commented line seems to indicate you
>> want it to do what `c.__dict__[T]` does, but you can already do that
>> with `setattr(c, T, 1)`.  Or you can just give c an attribute that's a
>> dict, but has an easier-to-type name than __dict__, so you can do
>> `c.mydict[T]`.  What is the specific advantage of `c.[T]` over these
>> existing solutions?
>>
> 
> Hmm... Why can't we just allow empty identifiers, and set a default
> handler for empty identifiers that implements the proposed ECS?

It sounds to me like the general shape of what you're proposing is
already entirely possible, just without the idiosyncratic syntax. You
could write a library that adds support for your components using some
other syntax without any additional language support.

I don't know, something like this should be doable:

@componentlib.has_components
class Car:
# ...

class Engine(componentlib.Component): # might have to be a metaclass?
def stall(self, car):
raise UnpleasantNoise()
# ...

car = Car()
car.engine = Engine()
car.engine.stall()
# and so on.

If you prefer square brackets, you can implement it with __getitem__
syntax instead of __getattr__ syntax.

The point is: not only does your proposal not *need* additional language
support; I'm not at all convinced that it would benefit from additional
language support.

> 
> But the basic idea is to indicate something at the call site, namely
> that T is a contract and the object returned should respect that
> contract and any function calls should pass the original object as an
> argument. (I personally don't like how Python treats o.m() (has self)
> the same as o.f() (no self) syntax-wise, either.)
> ___
> Python-ideas mailing list
> Python-ideas@python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/

___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-30 Thread Wes Turner
On Monday, October 30, 2017, Wes Turner  wrote:

> ... But interfaces are clunky and traits are lightweight, and this isn't
> Go, so we can't just create a class as a namespace full of @staticmethods
> which accept the relevant object references.
>
> * __setattribute__ -> __getitem__, __setitem__
>
> On Monday, October 30, 2017, Wes Turner  > wrote:
>
>>
>>
>> On Sunday, October 29, 2017, Nick Coghlan  wrote:
>>
>>> On 29 October 2017 at 12:25, Brendan Barnwell 
>>> wrote:
>>>
 On 2017-10-28 19:13, Soni L. wrote:

> And to have all cars have engines, you'd do:
>
> class Car:
> def __init__(self, ???):
>   self[Engine] = GasEngine()
>
> car = Car()
> car[Engine].kickstart() # kickstart gets the car as second argument.
>
> And if you can't do that, then you can't yet do what I'm proposing, and
> thus the proposal makes sense, even if it still needs some refining...
>

 As near as I can tell you can indeed do that, although it's
 still not clear to me why you'd want to.  You can give Car a __getitem__
 that on-the-fly generates an Engine object that knows which Car it is
 attached to, and then you can make Engine.kickstart a descriptor that knows
 which Engine it is attached to, and from that can figure out which Car it
 is attached to.

>>>
>>> Right, I think a few different things are getting confused here related
>>> to how different folks use composition.
>>>
>>> For most data modeling use cases, the composition model you want is
>>> either a tree or an acyclic graph, where the subcomponents don't know
>>> anything about the whole that they're a part of. This gives you good
>>> component isolation, and avoids circular dependencies.
>>>
>>> However, for other cases, you *do* want the child object to be aware of
>>> the parent - XML etrees are a classic example of this, where we want to
>>> allow navigation back up the tree, so each node gains a reference to its
>>> parent node. This often takes the form of a combination of delegation
>>> (parent->child references) and dependency inversion (child->parent
>>> reference).
>>>
>>
>> This is Java-y and maybe not opcode optimizable, but maybe there's a case
>> for defining __setattribute__ so that square brackets denote Rust-like
>> traits:
>>
>> https://docs.spring.io/spring-python/1.2.x/sphinx/html/objec
>> ts-pythonconfig.html#object-definition-inheritance
>>
>>   @Object(parent="request")
>>   def request_dev(self, req=None):
>>
>>   > Observe that in the following example the child definitions must
>> define an optional ‘req’ argument; in runtime they will be passed its value
>> basing on what their parent object will return.
>>
>> It's testable, but confusing to Java programmers who aren't familiar with
>> why Guice forces the patterns that it does:
>>
>> https://docs.spring.io/spring-python/1.2.x/sphinx/html/objec
>> ts-more.html#testable-code
>>
>> https://github.com/google/guice/wiki/Motivation#dependency-injection
>>
>> > Like the factory, dependency injection is just a design pattern. The
>> core principle is to separate behaviour from dependency resolution. In our
>> example, the RealBillingService is not responsible for looking up the
>> TransactionLog and CreditCardProcessor. Instead, they're passed in as
>> constructor parameters:
>>
>> When these are constructor parameters, we don't need to monkeypatch attrs
>> in order to write tests; which, IIUC, is also partly why you'd want
>> traits/mixins with the proposed special Rust-like syntax:
>>
>> https://docs.pytest.org/en/latest/monkeypatch.html
>>
>> https://docs.pytest.org/en/latest/fixture.html#modularity-
>> using-fixtures-from-a-fixture-function (this is too magic(), too)
>>
>> But you want dynamic mixins that have an upward reference and Rust-like
>> syntax (and no factories).
>>
>>
>>> For the car/engine example, this relates to explicitly modeling the
>>> relationship whereby a car can have one or more engines
>>>
>>
>> class MultiEngine():
>> zope.interface.implements(IEngine)
>>
>> https://zopeinterface.readthedocs.io/en/latest/README.html#
>> declaring-implemented-interfaces
>>
>> But interfaces aren't yet justified because it's only a few lines and
>> those are just documentation or a too-complex adapter registry dict, anyway.
>>
>>
>>>  (but the engine may not currently be installed),
>>>
>>
>> So it should default to a MockEngine which also implements(IEngine) and
>> raises NotImplementedError
>>
>>
>>>
>>>  while an engine can be installed in at most one car at any given point
>>> in time.
>>>
>>
>> But the refcounts would be too difficult
>>
>> This:
>>
>>
>>>
>>> You don't even need the descriptor protocol for that though, you just
>>> need the subcomponent to accept the parent reference as a constructor
>>> parameter:
>>>
>>> 

Re: [Python-ideas] Composition over Inheritance

2017-10-30 Thread Wes Turner
... But interfaces are clunky and traits are lightweight, and this isn't
Go, so we can't just create a class as a namespace full of @staticmethods
which accept the relevant object references.

* __setattribute__ -> __getitem__, __setitem__

On Monday, October 30, 2017, Wes Turner  wrote:

>
>
> On Sunday, October 29, 2017, Nick Coghlan  > wrote:
>
>> On 29 October 2017 at 12:25, Brendan Barnwell 
>> wrote:
>>
>>> On 2017-10-28 19:13, Soni L. wrote:
>>>
 And to have all cars have engines, you'd do:

 class Car:
 def __init__(self, ???):
   self[Engine] = GasEngine()

 car = Car()
 car[Engine].kickstart() # kickstart gets the car as second argument.

 And if you can't do that, then you can't yet do what I'm proposing, and
 thus the proposal makes sense, even if it still needs some refining...

>>>
>>> As near as I can tell you can indeed do that, although it's
>>> still not clear to me why you'd want to.  You can give Car a __getitem__
>>> that on-the-fly generates an Engine object that knows which Car it is
>>> attached to, and then you can make Engine.kickstart a descriptor that knows
>>> which Engine it is attached to, and from that can figure out which Car it
>>> is attached to.
>>>
>>
>> Right, I think a few different things are getting confused here related
>> to how different folks use composition.
>>
>> For most data modeling use cases, the composition model you want is
>> either a tree or an acyclic graph, where the subcomponents don't know
>> anything about the whole that they're a part of. This gives you good
>> component isolation, and avoids circular dependencies.
>>
>> However, for other cases, you *do* want the child object to be aware of
>> the parent - XML etrees are a classic example of this, where we want to
>> allow navigation back up the tree, so each node gains a reference to its
>> parent node. This often takes the form of a combination of delegation
>> (parent->child references) and dependency inversion (child->parent
>> reference).
>>
>
> This is Java-y and maybe not opcode optimizable, but maybe there's a case
> for defining __setattribute__ so that square brackets denote Rust-like
> traits:
>
> https://docs.spring.io/spring-python/1.2.x/sphinx/html/
> objects-pythonconfig.html#object-definition-inheritance
>
>   @Object(parent="request")
>   def request_dev(self, req=None):
>
>   > Observe that in the following example the child definitions must
> define an optional ‘req’ argument; in runtime they will be passed its value
> basing on what their parent object will return.
>
> It's testable, but confusing to Java programmers who aren't familiar with
> why Guice forces the patterns that it does:
>
> https://docs.spring.io/spring-python/1.2.x/sphinx/html/
> objects-more.html#testable-code
>
> https://github.com/google/guice/wiki/Motivation#dependency-injection
>
> > Like the factory, dependency injection is just a design pattern. The
> core principle is to separate behaviour from dependency resolution. In our
> example, the RealBillingService is not responsible for looking up the
> TransactionLog and CreditCardProcessor. Instead, they're passed in as
> constructor parameters:
>
> When these are constructor parameters, we don't need to monkeypatch attrs
> in order to write tests; which, IIUC, is also partly why you'd want
> traits/mixins with the proposed special Rust-like syntax:
>
> https://docs.pytest.org/en/latest/monkeypatch.html
>
> https://docs.pytest.org/en/latest/fixture.html#modularity-using-fixtures-
> from-a-fixture-function (this is too magic(), too)
>
> But you want dynamic mixins that have an upward reference and Rust-like
> syntax (and no factories).
>
>
>> For the car/engine example, this relates to explicitly modeling the
>> relationship whereby a car can have one or more engines
>>
>
> class MultiEngine():
> zope.interface.implements(IEngine)
>
> https://zopeinterface.readthedocs.io/en/latest/README.html#declaring-
> implemented-interfaces
>
> But interfaces aren't yet justified because it's only a few lines and
> those are just documentation or a too-complex adapter registry dict, anyway.
>
>
>>  (but the engine may not currently be installed),
>>
>
> So it should default to a MockEngine which also implements(IEngine) and
> raises NotImplementedError
>
>
>>
>>  while an engine can be installed in at most one car at any given point
>> in time.
>>
>
> But the refcounts would be too difficult
>
> This:
>
>
>>
>> You don't even need the descriptor protocol for that though, you just
>> need the subcomponent to accept the parent reference as a constructor
>> parameter:
>>
>> class Car:
>>   def __init__(self, engine_type):
>> self.engine = engine_type(self)
>>
>> However, this form of explicit dependency inversion wouldn't work as well
>> if you want to be able to 

Re: [Python-ideas] Composition over Inheritance

2017-10-30 Thread Wes Turner
On Sunday, October 29, 2017, Nick Coghlan  wrote:

> On 29 October 2017 at 12:25, Brendan Barnwell  > wrote:
>
>> On 2017-10-28 19:13, Soni L. wrote:
>>
>>> And to have all cars have engines, you'd do:
>>>
>>> class Car:
>>> def __init__(self, ???):
>>>   self[Engine] = GasEngine()
>>>
>>> car = Car()
>>> car[Engine].kickstart() # kickstart gets the car as second argument.
>>>
>>> And if you can't do that, then you can't yet do what I'm proposing, and
>>> thus the proposal makes sense, even if it still needs some refining...
>>>
>>
>> As near as I can tell you can indeed do that, although it's still
>> not clear to me why you'd want to.  You can give Car a __getitem__ that
>> on-the-fly generates an Engine object that knows which Car it is attached
>> to, and then you can make Engine.kickstart a descriptor that knows which
>> Engine it is attached to, and from that can figure out which Car it is
>> attached to.
>>
>
> Right, I think a few different things are getting confused here related to
> how different folks use composition.
>
> For most data modeling use cases, the composition model you want is either
> a tree or an acyclic graph, where the subcomponents don't know anything
> about the whole that they're a part of. This gives you good component
> isolation, and avoids circular dependencies.
>
> However, for other cases, you *do* want the child object to be aware of
> the parent - XML etrees are a classic example of this, where we want to
> allow navigation back up the tree, so each node gains a reference to its
> parent node. This often takes the form of a combination of delegation
> (parent->child references) and dependency inversion (child->parent
> reference).
>

This is Java-y and maybe not opcode optimizable, but maybe there's a case
for defining __setattribute__ so that square brackets denote Rust-like
traits:

https://docs.spring.io/spring-python/1.2.x/sphinx/html/objects-pythonconfig.html#object-definition-inheritance

  @Object(parent="request")
  def request_dev(self, req=None):

  > Observe that in the following example the child definitions must define
an optional ‘req’ argument; in runtime they will be passed its value basing
on what their parent object will return.

It's testable, but confusing to Java programmers who aren't familiar with
why Guice forces the patterns that it does:

https://docs.spring.io/spring-python/1.2.x/sphinx/html/objects-more.html#testable-code

https://github.com/google/guice/wiki/Motivation#dependency-injection

> Like the factory, dependency injection is just a design pattern. The core
principle is to separate behaviour from dependency resolution. In our
example, the RealBillingService is not responsible for looking up the
TransactionLog and CreditCardProcessor. Instead, they're passed in as
constructor parameters:

When these are constructor parameters, we don't need to monkeypatch attrs
in order to write tests; which, IIUC, is also partly why you'd want
traits/mixins with the proposed special Rust-like syntax:

https://docs.pytest.org/en/latest/monkeypatch.html

https://docs.pytest.org/en/latest/fixture.html#modularity-using-fixtures-from-a-fixture-function
(this is too magic(), too)

But you want dynamic mixins that have an upward reference and Rust-like
syntax (and no factories).


> For the car/engine example, this relates to explicitly modeling the
> relationship whereby a car can have one or more engines
>

class MultiEngine():
zope.interface.implements(IEngine)

https://zopeinterface.readthedocs.io/en/latest/README.html#declaring-implemented-interfaces

But interfaces aren't yet justified because it's only a few lines and those
are just documentation or a too-complex adapter registry dict, anyway.


>  (but the engine may not currently be installed),
>

So it should default to a MockEngine which also implements(IEngine) and
raises NotImplementedError


>
>  while an engine can be installed in at most one car at any given point in
> time.
>

But the refcounts would be too difficult

This:


>
> You don't even need the descriptor protocol for that though, you just need
> the subcomponent to accept the parent reference as a constructor parameter:
>
> class Car:
>   def __init__(self, engine_type):
> self.engine = engine_type(self)
>
> However, this form of explicit dependency inversion wouldn't work as well
> if you want to be able to explicitly create an "uninstalled engine"
> instance, and then pass the engine in as a parameter to the class
> constructor:
>
> class Car:
>   def __init__(self, engine):
> self.engine = engine # How would we ensure the engine is marked as
> installed here?
>
> As it turns out, Python doesn't need new syntax for this either, as it's
> all already baked into the regular attribute access syntax, whereby
> descriptor methods get passed a reference not only to the descriptor, 

Re: [Python-ideas] Composition over Inheritance

2017-10-29 Thread Nick Coghlan
On 29 October 2017 at 21:44, Soni L.  wrote:

> ORMs use this kind of descriptor based composition management extensively
> in order to reliably model database foreign key relationships in a way
> that's mostly transparent to users of the ORM classes.
>
>
> And this is how you miss the whole point of being able to dynamically
> add/remove arbitrary components on objects you didn't create, at runtime.
>

You can already do that by adding new properties to classes
post-definition, or by changing __class__ to refer to a different type, or
by wrapping objects in transparent proxy types the way wrapt does.

We *allow* that kind of thing, because it's sometimes beneficial in order
to get two libraries to play nicely together at runtime without having to
patch one or the other. However, it's a last resort option that you use
when you've exhausted the other more maintainable alternatives, not
something we actually want to encourage.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-29 Thread Soni L.



On 2017-10-29 02:57 PM, Brendan Barnwell wrote:

On 2017-10-29 04:44, Soni L. wrote:

And this is how you miss the whole point of being able to dynamically
add/remove arbitrary components on objects you didn't create, at 
runtime.


Someone gave me this code and told me it explains what I'm trying to do:
https://repl.it/NYCF/3

class T:
 pass

class C:
 pass

c = C()

#c.[T] = 1
c.__dict__[T] = 1


Again, can you please explain why you want to write c.[T]? What do 
you intend that to *do*?  Your commented line seems to indicate you 
want it to do what `c.__dict__[T]` does, but you can already do that 
with `setattr(c, T, 1)`.  Or you can just give c an attribute that's a 
dict, but has an easier-to-type name than __dict__, so you can do 
`c.mydict[T]`.  What is the specific advantage of `c.[T]` over these 
existing solutions?




Hmm... Why can't we just allow empty identifiers, and set a default 
handler for empty identifiers that implements the proposed ECS?


But the basic idea is to indicate something at the call site, namely 
that T is a contract and the object returned should respect that 
contract and any function calls should pass the original object as an 
argument. (I personally don't like how Python treats o.m() (has self) 
the same as o.f() (no self) syntax-wise, either.)

___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-29 Thread Brendan Barnwell

On 2017-10-29 04:44, Soni L. wrote:

And this is how you miss the whole point of being able to dynamically
add/remove arbitrary components on objects you didn't create, at runtime.

Someone gave me this code and told me it explains what I'm trying to do:
https://repl.it/NYCF/3

class T:
 pass

class C:
 pass

c = C()

#c.[T] = 1
c.__dict__[T] = 1


	Again, can you please explain why you want to write c.[T]?  What do you 
intend that to *do*?  Your commented line seems to indicate you want it 
to do what `c.__dict__[T]` does, but you can already do that with 
`setattr(c, T, 1)`.  Or you can just give c an attribute that's a dict, 
but has an easier-to-type name than __dict__, so you can do 
`c.mydict[T]`.  What is the specific advantage of `c.[T]` over these 
existing solutions?


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-29 Thread Koos Zevenhoven
(looks like you forgot to reply to the list)

On Sun, Oct 29, 2017 at 7:47 AM, Greg Ewing 
wrote:

> Koos Zevenhoven wrote:
>
>> It looks like you can just as easily drive the car without having the key
>>
>
> That's been found to be a problem in real life, too.
> More than one Python-programming car thief has been
> caught with this function in their personal library:
>
> def hotwire(car):
>car.engine.start()


​Yes, but my point was still about what the public API is. Starting the car
with the key should be easier than starting it without the key. Surely you
should be able to start the engine without the key if you look under the
hood and figure it out. And maybe you'd need a tool for that to be kept in
your garage for debugging, but not as a button attached to the outside of
the car or on the dashboard :-).

The problem becomes even worse if you instead make Car inherit from its
parts (including Engine), because the relationship of a car and and engine
is not well described by inheritance. A car is not a kind of engine, nor
should it pretend to be an implementation of an engine. A car *comprises*
(or contains) an engine.  And who knows, maybe it has two engines!

-- Koos


-- 
+ Koos Zevenhoven + http://twitter.com/k7hoven +
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-29 Thread Soni L.



On 2017-10-29 02:28 AM, Nick Coghlan wrote:
On 29 October 2017 at 12:25, Brendan Barnwell > wrote:


On 2017-10-28 19:13, Soni L. wrote:

And to have all cars have engines, you'd do:

class Car:
    def __init__(self, ???):
      self[Engine] = GasEngine()

car = Car()
car[Engine].kickstart() # kickstart gets the car as second
argument.

And if you can't do that, then you can't yet do what I'm
proposing, and
thus the proposal makes sense, even if it still needs some
refining...


        As near as I can tell you can indeed do that, although
it's still not clear to me why you'd want to. You can give Car a
__getitem__ that on-the-fly generates an Engine object that knows
which Car it is attached to, and then you can make
Engine.kickstart a descriptor that knows which Engine it is
attached to, and from that can figure out which Car it is attached to.


Right, I think a few different things are getting confused here 
related to how different folks use composition.


For most data modeling use cases, the composition model you want is 
either a tree or an acyclic graph, where the subcomponents don't know 
anything about the whole that they're a part of. This gives you good 
component isolation, and avoids circular dependencies.


However, for other cases, you *do* want the child object to be aware 
of the parent - XML etrees are a classic example of this, where we 
want to allow navigation back up the tree, so each node gains a 
reference to its parent node. This often takes the form of a 
combination of delegation (parent->child references) and dependency 
inversion (child->parent reference).


For the car/engine example, this relates to explicitly modeling the 
relationship whereby a car can have one or more engines (but the 
engine may not currently be installed), while an engine can be 
installed in at most one car at any given point in time.


You don't even need the descriptor protocol for that though, you just 
need the subcomponent to accept the parent reference as a constructor 
parameter:


    class Car:
      def __init__(self, engine_type):
    self.engine = engine_type(self)

However, this form of explicit dependency inversion wouldn't work as 
well if you want to be able to explicitly create an "uninstalled 
engine" instance, and then pass the engine in as a parameter to the 
class constructor:


    class Car:
      def __init__(self, engine):
    self.engine = engine # How would we ensure the engine is 
marked as installed here?


As it turns out, Python doesn't need new syntax for this either, as 
it's all already baked into the regular attribute access syntax, 
whereby descriptor methods get passed a reference not only to the 
descriptor, but *also* to the object being accessed: 
https://docs.python.org/3/howto/descriptor.html#descriptor-protocol


And then the property builtin lets you ignore the existence of the 
descriptor object entirely, and only care about the original object, 
allowing the above example to be written as:


    class Car:
      def __init__(self, engine):
    self.engine = engine # This implicitly marks the engine as 
installed


  @property
  def engine(self):
  return self._engine

  @engine.setter
  def engine(self, engine):
 if engine is not None:
         if self._engine is not None:
             raise RuntimeError("Car already has an engine installed")
         if engine._car is not None:
         raise RuntimeError("Engine is already installed in 
another car")

         engine._car = self
 self._engine = engine

car = Car(GasEngine())

ORMs use this kind of descriptor based composition management 
extensively in order to reliably model database foreign key 
relationships in a way that's mostly transparent to users of the ORM 
classes.


And this is how you miss the whole point of being able to dynamically 
add/remove arbitrary components on objects you didn't create, at runtime.


Someone gave me this code and told me it explains what I'm trying to do: 
https://repl.it/NYCF/3


class T:
    pass

class C:
    pass

c = C()

#c.[T] = 1
c.__dict__[T] = 1

I'd also like to add:

def someone_elses_lib_function(arbitrary_object):
  #arbitrary_object.[T] = object()
  arbitrary_object.__dict__[T] = object()

and

def another_ones_lib_function(arbitrary_object):
  #if arbitrary_object.[T]:
  if arbitrary_object.__dict__[T]:
    #arbitrary_object.[T].thing()
    arbitrary_object.__dict__[T].thing(arbitrary_object)



Cheers,
Nick.

--
Nick Coghlan   | ncogh...@gmail.com    | 
Brisbane, Australia



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: 

Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Nick Coghlan
On 29 October 2017 at 12:25, Brendan Barnwell  wrote:

> On 2017-10-28 19:13, Soni L. wrote:
>
>> And to have all cars have engines, you'd do:
>>
>> class Car:
>> def __init__(self, ???):
>>   self[Engine] = GasEngine()
>>
>> car = Car()
>> car[Engine].kickstart() # kickstart gets the car as second argument.
>>
>> And if you can't do that, then you can't yet do what I'm proposing, and
>> thus the proposal makes sense, even if it still needs some refining...
>>
>
> As near as I can tell you can indeed do that, although it's still
> not clear to me why you'd want to.  You can give Car a __getitem__ that
> on-the-fly generates an Engine object that knows which Car it is attached
> to, and then you can make Engine.kickstart a descriptor that knows which
> Engine it is attached to, and from that can figure out which Car it is
> attached to.
>

Right, I think a few different things are getting confused here related to
how different folks use composition.

For most data modeling use cases, the composition model you want is either
a tree or an acyclic graph, where the subcomponents don't know anything
about the whole that they're a part of. This gives you good component
isolation, and avoids circular dependencies.

However, for other cases, you *do* want the child object to be aware of the
parent - XML etrees are a classic example of this, where we want to allow
navigation back up the tree, so each node gains a reference to its parent
node. This often takes the form of a combination of delegation
(parent->child references) and dependency inversion (child->parent
reference).

For the car/engine example, this relates to explicitly modeling the
relationship whereby a car can have one or more engines (but the engine may
not currently be installed), while an engine can be installed in at most
one car at any given point in time.

You don't even need the descriptor protocol for that though, you just need
the subcomponent to accept the parent reference as a constructor parameter:

class Car:
  def __init__(self, engine_type):
self.engine = engine_type(self)

However, this form of explicit dependency inversion wouldn't work as well
if you want to be able to explicitly create an "uninstalled engine"
instance, and then pass the engine in as a parameter to the class
constructor:

class Car:
  def __init__(self, engine):
self.engine = engine # How would we ensure the engine is marked as
installed here?

As it turns out, Python doesn't need new syntax for this either, as it's
all already baked into the regular attribute access syntax, whereby
descriptor methods get passed a reference not only to the descriptor, but
*also* to the object being accessed:
https://docs.python.org/3/howto/descriptor.html#descriptor-protocol

And then the property builtin lets you ignore the existence of the
descriptor object entirely, and only care about the original object,
allowing the above example to be written as:

class Car:
  def __init__(self, engine):
self.engine = engine # This implicitly marks the engine as installed

  @property
  def engine(self):
  return self._engine

  @engine.setter
  def engine(self, engine):
 if engine is not None:
 if self._engine is not None:
 raise RuntimeError("Car already has an engine installed")
 if engine._car is not None:
 raise RuntimeError("Engine is already installed in another
car")
 engine._car = self
 self._engine = engine

 car = Car(GasEngine())

ORMs use this kind of descriptor based composition management extensively
in order to reliably model database foreign key relationships in a way
that's mostly transparent to users of the ORM classes.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Brendan Barnwell

On 2017-10-28 19:13, Soni L. wrote:

Hmm thinking about it some more, this whole "magic()" thing is still bad.

Replace class Car with:

class Car:
pass
# or something like that

and use it as:

car = Car()
car[Engine] = GasEngine() # please use the actual type instead of a
stringy type for this.
car[Engine].kickstart() # kickstart gets the car as second argument.

And to have all cars have engines, you'd do:

class Car:
def __init__(self, ???):
  self[Engine] = GasEngine()

car = Car()
car[Engine].kickstart() # kickstart gets the car as second argument.

And if you can't do that, then you can't yet do what I'm proposing, and
thus the proposal makes sense, even if it still needs some refining...


	As near as I can tell you can indeed do that, although it's still not 
clear to me why you'd want to.  You can give Car a __getitem__ that 
on-the-fly generates an Engine object that knows which Car it is 
attached to, and then you can make Engine.kickstart a descriptor that 
knows which Engine it is attached to, and from that can figure out which 
Car it is attached to.


	But why?  Why do you want to use this particular syntax to do these 
things?  You haven't explained what semantics you're attaching to the 
[brackets] as opposed to the .attribute notation.  Also, why do you want 
the car to be the second argument?  What if the components are more 
deeply nested?  Are you going to pass every parent component as a 
separate argument?  Why not just have the call be made on an object that 
has the car available as an attribute (so that inside kickstart() you 
can access self.car or something)?


	The pandas library, for instance, makes heavy use of descriptors along 
these lines.  A DataFrame object has attributes called .loc and .iloc 
that you use to do different kinds of indexing with things like 
my_data_frame.loc['this':'that'] .  This appears conceptually similar to 
what you're describing here.  But it's hard to tell, because you still 
haven't given a clear, direct example of how you would use what you're 
proposed to actually accomplish some task.


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Chris Angelico
On Sun, Oct 29, 2017 at 1:05 PM, Soni L.  wrote:
> And how do you make the object creation so cheap to the point where it's
> actually practical? (quick question: does Python use a single opcode and an
> optimized codepath for method calls, or does it always create a method
> wrapper, even for immediate o.m() calls? If it's the latter then yeah I
> guess there's no reason for new syntax because it's not gonna be
> significantly slower than what we currently have...)

Python-the-language simply specifies the semantics. Different
implementations do different things. AIUI CPython 3.7 always creates a
method wrapper (using a free-list to minimize memory allocations), but
a future version might not; and PyPy's current versions have a special
opcode.

ChrisA
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Soni L.



On 2017-10-29 12:05 AM, Soni L. wrote:



On 2017-10-28 11:57 PM, Chris Angelico wrote:

On Sun, Oct 29, 2017 at 12:46 PM, Soni L.  wrote:

But you should be able to do things like

car = object()
car[Engine] = SimpleEngine()
car.[Engine].kickstart() # calls kickstart method with an instance of
SimpleEngine as `self`/first argument and `car` as second argument.
# etc

Which your decorator-based approach quite obviously doesn't let you.

I think I follow what you're trying to do here. You want to have a way
to refer to a subobject while letting it know about the parent. We
already have something like that: when you call a function that was
attached to a class, it gets to know which instance of that class you
used to locate that function. Maybe there's a way to use descriptor
protocol for this too?

class SimpleEngine:
 def kickstart(self, caller):
 """*boot* Engine starts"""

class Car:
 Engine = magic(SimpleEngine)

car = Car()

When you look up car.Engine, it remembers a reference to car. Then you
look up any callable from there, and it automatically provides an
additional parameter. I'm not sure how the details would work, but in
theory, this should be possible, right?


And how do you make the object creation so cheap to the point where 
it's actually practical? (quick question: does Python use a single 
opcode and an optimized codepath for method calls, or does it always 
create a method wrapper, even for immediate o.m() calls? If it's the 
latter then yeah I guess there's no reason for new syntax because it's 
not gonna be significantly slower than what we currently have...)


Hmm thinking about it some more, this whole "magic()" thing is still bad.

Replace class Car with:

class Car:
  pass
# or something like that

and use it as:

car = Car()
car[Engine] = GasEngine() # please use the actual type instead of a 
stringy type for this.

car[Engine].kickstart() # kickstart gets the car as second argument.

And to have all cars have engines, you'd do:

class Car:
  def __init__(self, ???):
    self[Engine] = GasEngine()

car = Car()
car[Engine].kickstart() # kickstart gets the car as second argument.

And if you can't do that, then you can't yet do what I'm proposing, and 
thus the proposal makes sense, even if it still needs some refining...






ChrisA
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Soni L.



On 2017-10-28 11:57 PM, Chris Angelico wrote:

On Sun, Oct 29, 2017 at 12:46 PM, Soni L.  wrote:

But you should be able to do things like

car = object()
car[Engine] = SimpleEngine()
car.[Engine].kickstart() # calls kickstart method with an instance of
SimpleEngine as `self`/first argument and `car` as second argument.
# etc

Which your decorator-based approach quite obviously doesn't let you.

I think I follow what you're trying to do here. You want to have a way
to refer to a subobject while letting it know about the parent. We
already have something like that: when you call a function that was
attached to a class, it gets to know which instance of that class you
used to locate that function. Maybe there's a way to use descriptor
protocol for this too?

class SimpleEngine:
 def kickstart(self, caller):
 """*boot* Engine starts"""

class Car:
 Engine = magic(SimpleEngine)

car = Car()

When you look up car.Engine, it remembers a reference to car. Then you
look up any callable from there, and it automatically provides an
additional parameter. I'm not sure how the details would work, but in
theory, this should be possible, right?


And how do you make the object creation so cheap to the point where it's 
actually practical? (quick question: does Python use a single opcode and 
an optimized codepath for method calls, or does it always create a 
method wrapper, even for immediate o.m() calls? If it's the latter then 
yeah I guess there's no reason for new syntax because it's not gonna be 
significantly slower than what we currently have...)




ChrisA
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Chris Angelico
On Sun, Oct 29, 2017 at 12:46 PM, Soni L.  wrote:
> But you should be able to do things like
>
> car = object()
> car[Engine] = SimpleEngine()
> car.[Engine].kickstart() # calls kickstart method with an instance of
> SimpleEngine as `self`/first argument and `car` as second argument.
> # etc
>
> Which your decorator-based approach quite obviously doesn't let you.

I think I follow what you're trying to do here. You want to have a way
to refer to a subobject while letting it know about the parent. We
already have something like that: when you call a function that was
attached to a class, it gets to know which instance of that class you
used to locate that function. Maybe there's a way to use descriptor
protocol for this too?

class SimpleEngine:
def kickstart(self, caller):
"""*boot* Engine starts"""

class Car:
Engine = magic(SimpleEngine)

car = Car()

When you look up car.Engine, it remembers a reference to car. Then you
look up any callable from there, and it automatically provides an
additional parameter. I'm not sure how the details would work, but in
theory, this should be possible, right?

ChrisA
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Soni L.



On 2017-10-28 11:31 PM, Koos Zevenhoven wrote:
On Sat, Oct 28, 2017 at 11:24 PM, Soni L. >wrote:



On 2017-10-28 02:51 PM, Steven D'Aprano wrote:


You ignored my question: Is that the sort of thing you mean by
composition? If not, then what do you mean by it? This is not a
rhetorical question: I'm having difficulty understanding your
proposal.
It is too vague, and you are using terminology in ways I don't
understand.

Maybe that's my ignorance, or maybe you're using non-standard
terminology. Either way, if I'm having trouble, probably
others are too.
Help us understand your proposal.



I have to say I'm almost impressed by the constructiveness of the 
discussion, even though​ I still don't understand the point of all the 
square brackets in the proposal.


With composition, you can have car.key.turn() call
car.ignition.start(), without having to add car to key or ignition
to key. You just have to put both in a car and they can then see
eachother!


Here it's a bit confusing that the key is thought of as part of the 
car. It's easy to imagine that an owner of two cars would want the 
same key to work for both of them. Or that one car could have multiple 
users with non-identical keys. I'm not sure if these things already 
exist in real life, but if not, it's probably just a matter of time.


But let's ignore this confusion for a moment, and imagine that the 
example makes perfect sense. Now, it sounds like you want something 
like namespacing for methods and attributes within a complicated 
class. Maybe you could implement it using nested classes and 
decorators to make sure 'self' is passed to to the methods in the way 
you want. The usage might look roughly like:


@namespacedclass
class Car:
@classnamespace
    class ignition:
        def start(self):
             ...

@classnamespace
    class key:
        def turn(self):
self.ignition.start()


Another concern regarding the example, however, is that this seems to 
make it unclear what the public API of the car is. It looks like you 
can just as easily drive the car without having the key: just call 
car.ignition.start().


It's a crap example, yes.

But you should be able to do things like

car = object()
car[Engine] = SimpleEngine()
car.[Engine].kickstart() # calls kickstart method with an instance of 
SimpleEngine as `self`/first argument and `car` as second argument.

# etc

Which your decorator-based approach quite obviously doesn't let you.

​

-- Koos


--
+ Koos Zevenhoven + http://twitter.com/k7hoven +


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Koos Zevenhoven
On Sat, Oct 28, 2017 at 11:24 PM, Soni L.  wrote:

>
> On 2017-10-28 02:51 PM, Steven D'Aprano wrote:
>
>>
>> You ignored my question: Is that the sort of thing you mean by
>> composition? If not, then what do you mean by it? This is not a
>> rhetorical question: I'm having difficulty understanding your proposal.
>> It is too vague, and you are using terminology in ways I don't
>> understand.
>>
>> Maybe that's my ignorance, or maybe you're using non-standard
>> terminology. Either way, if I'm having trouble, probably others are too.
>> Help us understand your proposal.
>>
>
>
I have to say I'm almost impressed by the constructiveness of the
discussion, even though​ I still don't understand the point of all the
square brackets in the proposal.


> With composition, you can have car.key.turn() call car.ignition.start(),
> without having to add car to key or ignition to key. You just have to put
> both in a car and they can then see eachother!
>

Here it's a bit confusing that the key is thought of as part of the car.
It's easy to imagine that an owner of two cars would want the same key to
work for both of them. Or that one car could have multiple users with
non-identical keys. I'm not sure if these things already exist in real
life, but if not, it's probably just a matter of time.

But let's ignore this confusion for a moment, and imagine that the example
makes perfect sense. Now, it sounds like you want something like
namespacing for methods and attributes within a complicated class. Maybe
you could implement it using nested classes and decorators to make sure
'self' is passed to to the methods in the way you want. The usage might
look roughly like:

@namespacedclass
class Car:
@classnamespace
class ignition:
def start(self):
 ...

@classnamespace
class key:
def turn(self):
self.ignition.start()


Another concern regarding the example, however, is that this seems to make
it unclear what the public API of the car is. It looks like you can just as
easily drive the car without having the key: just call car.ignition.start().
​

-- Koos


-- 
+ Koos Zevenhoven + http://twitter.com/k7hoven +
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Soni L.



On 2017-10-28 02:51 PM, Steven D'Aprano wrote:

On Sat, Oct 28, 2017 at 10:19:09AM -0200, Soni L. wrote:


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()?

You don't -- the key is not a component of the car, its an argument of
ignition.start. If the key doesn't fit, ignition.start() raises an
exception and the car doesn't start.

I'm not really interested in getting into a tedious debate over the best
way to design a Car object. As I said, the above is just a loose sketch
illustrating composition, not a carefully planned and debugged object.
The aim here is to understand your proposal: what exactly do you mean
for Python to support composition over inheritence, and how does it
differ from Python's existing support for composition?

You ignored my question: Is that the sort of thing you mean by
composition? If not, then what do you mean by it? This is not a
rhetorical question: I'm having difficulty understanding your proposal.
It is too vague, and you are using terminology in ways I don't
understand.

Maybe that's my ignorance, or maybe you're using non-standard
terminology. Either way, if I'm having trouble, probably others are too.
Help us understand your proposal.


With composition, you can have car.key.turn() call car.ignition.start(), 
without having to add car to key or ignition to key. You just have to 
put both in a car and they can then see eachother!






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

What do you mean by "conflict-free interfaces"?

I can only repeat my earlier request:


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.


In Rust, you can put as many "conflicting" traits as you want on the 
same object, and it'll still work! It'll compile, it'll run, you'll be 
able to use the object in existing code that expects a specific trait, 
and code that operates on the object itself is fairly easy to write.





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()?

Maybe you can't. Maybe this is a crippling example of why composition
isn't as good as inheritence and the OOP community is right that
inheritence is the best thing since sliced bread. Maybe my design of the
Car object sucks.

But who cares? None of this comes any closer to explaining your
proposal.


Composition works fine, if you do it like Rust.





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.

One of us is using non-standard terminology, and I don't think it is me.
(Happy to be corrected if I'm wrong.)

I understand that composition and delegation go hand in hand: you can't
have one without the other. Composition refers to the arrangement of an
object that is composed of other objects. Delegation refers to the way
that the compound object calls methods on the component objects.

The point (as I understand it) of composition is that a Car doesn't just
have an Engine, it delegates functionality to the Engine: the Car object
derives functionality by calling Engine methods directly, rather than
inheriting them. Car.forward() delegates to Engine.forward().

The point is that the implementation of Car.forward is found in the
self.engine object, rather than being inherited from an Engine class.

Without delegation, the components aren't components at all, merely data
attributes: Car.colour = 'red'.

Does this match your proposal? If not, how is your proposal different?


Meet ECS:

https://en.wikipedia.org/wiki/Entity_component_system

Now, keep in mind, the 

Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Steven D'Aprano
On Sat, Oct 28, 2017 at 10:19:09AM -0200, Soni L. wrote:

> >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()?

You don't -- the key is not a component of the car, its an argument of 
ignition.start. If the key doesn't fit, ignition.start() raises an 
exception and the car doesn't start.

I'm not really interested in getting into a tedious debate over the best 
way to design a Car object. As I said, the above is just a loose sketch 
illustrating composition, not a carefully planned and debugged object. 
The aim here is to understand your proposal: what exactly do you mean 
for Python to support composition over inheritence, and how does it 
differ from Python's existing support for composition?

You ignored my question: Is that the sort of thing you mean by 
composition? If not, then what do you mean by it? This is not a 
rhetorical question: I'm having difficulty understanding your proposal. 
It is too vague, and you are using terminology in ways I don't 
understand.

Maybe that's my ignorance, or maybe you're using non-standard 
terminology. Either way, if I'm having trouble, probably others are too. 
Help us understand your proposal.


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

What do you mean by "conflict-free interfaces"?

I can only repeat my earlier request:

> >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()?

Maybe you can't. Maybe this is a crippling example of why composition 
isn't as good as inheritence and the OOP community is right that 
inheritence is the best thing since sliced bread. Maybe my design of the 
Car object sucks.

But who cares? None of this comes any closer to explaining your 
proposal.


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

One of us is using non-standard terminology, and I don't think it is me. 
(Happy to be corrected if I'm wrong.)

I understand that composition and delegation go hand in hand: you can't 
have one without the other. Composition refers to the arrangement of an 
object that is composed of other objects. Delegation refers to the way 
that the compound object calls methods on the component objects.

The point (as I understand it) of composition is that a Car doesn't just 
have an Engine, it delegates functionality to the Engine: the Car object 
derives functionality by calling Engine methods directly, rather than 
inheriting them. Car.forward() delegates to Engine.forward().

The point is that the implementation of Car.forward is found in the 
self.engine object, rather than being inherited from an Engine class.

Without delegation, the components aren't components at all, merely data 
attributes: Car.colour = 'red'.

Does this match your proposal? If not, how is your proposal different?


> 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`.)

That sounds like a hybrid of Lieberman-style delegation and the more 
common form. At first glance, that seems to add complexity without 
giving the advantages of either form of delegation.


> >>as that solves a lot of current issues
> >>relating to inheritance while 

Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Thomas Jollans
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 

Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Soni L.



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/




___
Python-ideas mailing list
Python-ideas@python.org

Re: [Python-ideas] Composition over Inheritance

2017-10-28 Thread Steven D'Aprano
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?


> 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?


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



> 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/


> 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?


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

http://www.artima.com/weblogs/viewpost.jsp?thread=246488

> [1] https://bitbucket.org/TeamSoni/cratera
> [2] https://www.rust-lang.org/


-- 
Steve
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Composition over Inheritance

2017-10-28 Thread Soni L.
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.


I am not sure how you'd set components, or test for components, 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] ?).


Thus I think o.[c].m() should be syntax sugar for o[c].m(o), with o 
being evaluated only once, 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.


(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. Note however that the original plans for Cratera were 
far more flexible, including allowing the "problematic" o.[c1].[c2].m() 
and o.[c][x].m(). I can go into more detail on how those should work, if 
wanted, but implementation-wise it's not looking good.)


[1] https://bitbucket.org/TeamSoni/cratera
[2] https://www.rust-lang.org/
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/