[PEAK] Using attribute bindings within constructor when using Component Factories

Phillip J. Eby pje at telecommunity.com
Sun Jan 18 13:38:57 EST 2004


At 12:49 AM 1/18/04 -0600, Wayne Larsen wrote:
>Thanks for the response,
>
>The problem I had with assembly events, is that there does not seem to be 
>any guarantee of order given an inheritance structure.  So, for example:
>
>class TestService(binding.Component):
>     protocols.advise(
>         instancesProvide=[ITestService]
>         )
>
>     log = binding.Obtain('logger:TestService')
>     message = binding.Obtain(PropertyName('helloworld.message'))
>
>     def __init(self):
>         self.log.info('from Parent __init: %s' % self.message)
>
>     __init = binding.Make(__init, uponAssembly=1)
>
>     def hello(self):
>         self.log.info(self.message)
>
>
>class ChildTestService(TestService):
>     def __init(self):
>         self.log.info('from Child __init: %s' % self.message)
>
>     __init = binding.Make(__init, uponAssembly=1)

There's a simple way to fix that.

class ChildTestService(TestService):
     def __init(self):
         self._TestService__init   # don't call, just reference!
         self.log.info('from Child __init: %s' % self.message)

What this does is access the inherited __init attribute.  If it has already 
run, then nothing happens.  If it has not already run, it will be run.  In 
other words, we're replacing inheritance dependency with *data* dependency.


>The problem here is converting an existing application.  The "normal" way 
>of doing this is to put all intialization into your constructor. This 
>initialization means setting themselves up based on data that I want to 
>move into config files.   If those classes depend on the base class 
>initalizing first, I am unsure whether I can rely on assembly events.  Is 
>there any guarantee of order for assembly events?

No, none whatsoever.  But if you access data that's needed, it will be 
there.  Think of it as a spreadsheet, where any cell (attribute) may refer 
to (depend on) any other cell (attribute), and the needed cell (attribute) 
will be calculated first.

So, if you're translating object that proactively set up attributes based 
on input data, you need to rethink them in terms of how they provide the 
output.  Instead of:

class Foo:
     def __init__(self,x,y):
         self.x = x
         self.y = y
         self.z = x + y
         self.q = x * y

think:

class Foo(binding.Component):
     x = binding.Require("A number", adaptTo=int)
     y = binding.Require("Another number", adaptTo=int)
     z = binding.Make(lambda self: self.x+self.y)
     q = binding.Make(lambda self: self.x*self.y)

Note also that this does *not* require assembly events.  Assembly events 
are actually a pretty specialized thing, intended for when you have 
components that want to seek out other components they need to "register" 
with.  For example, 'AdaptiveTask' classes want to register with the 
nearest ITaskQueue, so they have an assembly event to do that.  If you are 
using assembly events merely to initialize the object's data, you're 
probably doing something wrong.  Assembly events are for *behavior* that 
needs to occur when you plug a component into a system.

If you're adapting existing application classes, note that you don't 
necessarily have to make them into components.  Suppose we wanted to wrap 
the "legacy" Foo class above, that expects x and y to be supplied at 
__init__ time.  We could do:

class FooComponent(binding.Component):

     x = binding.Require("A number", adaptTo=int)
     y = binding.Require("Another number", adaptTo=int)

     _foo = binding.Make(lambda self: Foo(self.x,self.y)

     z = q = binding.Delegate('_foo')

So here, we delegate our 'z' and 'q' attributes to the '_foo' attribute, 
which will automatically be created from our 'x' and 'y' attributes if/when 
we need it.  (Note: Delegate only delegates attribute *reads*, not writes.)

Anyway, as you can see, if you have code you want to reuse, you can simply 
create replacement components like this, without having to fully translate 
them to a data-driven design.  Or, you can go all the way to a data-driven 
design.




More information about the PEAK mailing list