<br><br>On Sunday, October 29, 2017, Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 29 October 2017 at 12:25, Brendan Barnwell <span dir="ltr"><<a href="javascript:_e(%7B%7D,'cvml','brenbarn@brenbarn.net');" target="_blank">brenbarn@brenbarn.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span>On 2017-10-28 19:13, Soni L. wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
And to have all cars have engines, you'd do:<br>
<br>
class Car:<br>
def __init__(self, ???):<br>
self[Engine] = GasEngine()<br>
<br>
car = Car()<br>
car[Engine].kickstart() # kickstart gets the car as second argument.<br>
<br>
And if you can't do that, then you can't yet do what I'm proposing, and<br>
thus the proposal makes sense, even if it still needs some refining...<br>
</blockquote>
<br></span>
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.<br></blockquote><div><br></div>Right, I think a few different things are getting confused here related to how different folks use composition.</div><div class="gmail_quote"><br></div><div class="gmail_quote">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.</div><div class="gmail_quote"><br></div><div class="gmail_quote">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).</div></div></div></blockquote><div><br></div><div>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:</div><div><br></div><div><a href="https://docs.spring.io/spring-python/1.2.x/sphinx/html/objects-pythonconfig.html#object-definition-inheritance">https://docs.spring.io/spring-python/1.2.x/sphinx/html/objects-pythonconfig.html#object-definition-inheritance</a><br></div><div><br></div><div> @Object(parent="request")<br></div><div> def request_dev(self, req=None):</div><div><br></div><div> > 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.</div><div><br></div><div>It's testable, but confusing to Java programmers who aren't familiar with why Guice forces the patterns that it does:</div><div><br></div><div><a href="https://docs.spring.io/spring-python/1.2.x/sphinx/html/objects-more.html#testable-code">https://docs.spring.io/spring-python/1.2.x/sphinx/html/objects-more.html#testable-code</a><br></div><div> </div><div><a href="https://github.com/google/guice/wiki/Motivation#dependency-injection">https://github.com/google/guice/wiki/Motivation#dependency-injection</a><br></div><div><br></div><div>> 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:</div><div><br></div><div>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:</div><div><br></div><div><a href="https://docs.pytest.org/en/latest/monkeypatch.html">https://docs.pytest.org/en/latest/monkeypatch.html</a><br></div><div><br></div><div><a href="https://docs.pytest.org/en/latest/fixture.html#modularity-using-fixtures-from-a-fixture-function">https://docs.pytest.org/en/latest/fixture.html#modularity-using-fixtures-from-a-fixture-function</a> (this is too magic(), too)<br></div><div><br></div><div>But you want dynamic mixins that have an upward reference and Rust-like syntax (and no factories).</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote">For the car/engine example, this relates to explicitly modeling the relationship whereby a car can have one or more engines</div></div></div></blockquote><div> </div><div>class MultiEngine():</div><div> zope.interface.implements(IEngine) </div><div><br></div><div><a href="https://zopeinterface.readthedocs.io/en/latest/README.html#declaring-implemented-interfaces">https://zopeinterface.readthedocs.io/en/latest/README.html#declaring-implemented-interfaces</a><br></div><div><br></div><div>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.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote"> (but the engine may not currently be installed),</div></div></div></blockquote><div><br></div><div>So it should default to a MockEngine which also implements(IEngine) and raises NotImplementedError</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote"> while an engine can be installed in at most one car at any given point in time.</div></div></div></blockquote><div><br></div><div>But the refcounts would be too difficult</div><div><br></div><div>This:</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote">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:<br></div><div class="gmail_quote"><br></div><div class="gmail_quote"><div class="gmail_quote"> class Car:<br> def __init__(self, engine_type):<br>
self.engine = engine_type(self)</div><br><div class="gmail_quote">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:</div><div class="gmail_quote"><br></div><div class="gmail_quote"><div class="gmail_quote"> class Car:<br> def __init__(self, engine):<br>
self.engine = engine # How would we ensure the engine is marked as installed here?<br></div></div></div><div class="gmail_quote"><br></div><div class="gmail_quote">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: <a href="https://docs.python.org/3/howto/descriptor.html#descriptor-protocol" target="_blank">https://docs.python.org/3/<wbr>howto/descriptor.html#<wbr>descriptor-protocol</a></div></div></div></blockquote><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote">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:</div><div class="gmail_quote"><br></div><div class="gmail_quote"><div class="gmail_quote"><div class="gmail_quote"> class Car:<br> def __init__(self, engine):<br>
self.engine = engine # This implicitly marks the engine as installed<br></div></div><div class="gmail_quote"><br></div></div> @property</div><div class="gmail_extra"> def engine(self):</div><div class="gmail_extra"> return self._engine</div><div class="gmail_extra"><br></div><div class="gmail_extra"> @engine.setter</div><div class="gmail_extra"> def engine(self, engine):</div><div class="gmail_extra"> if engine is not None:<br></div><div class="gmail_extra"> if self._engine is not None:</div><div class="gmail_extra"> raise RuntimeError("Car already has an engine installed")<br></div><div class="gmail_extra"><div class="gmail_extra"> if engine._car is not None:</div><div class="gmail_extra"> raise RuntimeError("Engine is already installed in another car")</div><div class="gmail_extra"><div class="gmail_extra"> engine._car = self<br><div class="gmail_extra"></div> self._engine = engine<br></div><div class="gmail_extra"></div></div></div><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote">
car = Car(GasEngine())</div></div></div></blockquote><div><br></div><div>This could be less verbose. And less likely to raise a RuntimeError.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><br></div><div class="gmail_quote">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.<br></div></div></div></blockquote><div><br></div><div>So there's a 'factory' which passes the ref as a constructor parameter for such ORM instances; but they generally don't need to be dynamically modified at runtime because traits.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><br><div class="gmail_quote">Cheers,</div><div class="gmail_quote">Nick.<br></div><div class="gmail_quote"></div><br>-- <br><div>Nick Coghlan | <a href="javascript:_e(%7B%7D,'cvml','ncoghlan@gmail.com');" target="_blank">ncoghlan@gmail.com</a> | Brisbane, Australia</div>
</div></div>
</blockquote>