Credit: Eduard Hiti
You need to multiplex messages (attribute requests) to several objects that share the same interface.
As usual, this task is best wrapped in a class:
import operator # faster in Python 2.2, but we also handle any release from 2.0 and later try: dict except: from UserDict import UserDict as dict class Multiplex(dict): """ Multiplex messages to registered objects """ def _ _init_ _(self, objs=[]): dict._ _init_ _(self) for alias, obj in objs: self[alias] = obj def _ _call_ _(self, *args, **kwargs): """ Call registered objects and return results through another Multiplex. """ return self._ _class_ _( [ (alias, obj(*args, **kwargs)) for alias, obj in self.items( ) ] ) def _ _nonzero_ _(self): """ A Multiplex is true if all registered objects are true. """ return reduce(operator.and_, self.values( ), 1) def _ _getattr_ _(self, name): """ Wrap requested attributes for further processing. """ try: return dict._ _getattr_ _(self, name) except: # Return another Multiplex of the requested attributes return self._ _class_ _( [ (alias, getattr(obj, name) ) for alias, obj in self.items( ) ] )
As usual, this module is also invokable as a script, and, when run that way, supplies a self-test (or, here, a demo/example):
if _ _name_ _ == "_ _main_ _": import StringIO file1 = StringIO.StringIO( ) file2 = StringIO.StringIO( ) delegate = Multiplex( ) delegate[id(file1)] = file1 delegate[id(file2)] = file2 assert not delegate.closed delegate.write("Testing") assert file1.getvalue() == file2.getvalue( ) == "Testing" delegate.close( ) assert delegate.closed print "Test complete"
A Multiplex
object exposes the same interface as the
multiplexed registered object targets.
Multiplexing doesn’t
work for the dictionary interface, since that is used by the
Multiplex
class itself. We take care to ensure
that all attributes of a dictionary object are indeed accessed in the
way one deals with dictionaries. Note that this interferes with
delegating such attribute names as 'items'
,
'keys'
, 'values'
, and
'get'
. If this is a problem for your application,
you can avoid inheriting Multiplex
from
dict
, have Multiplex
use a
dict
by containment instead, and give it another
interface. However, whatever names you do decide to put on the public
interface will still not be subject to multiplexed delegation.
Attributes of individual registered objects can be accessed by the alias used to register them for multiplexed delegation:
delegate["test"] = aClass( ) print delegate.aClassAttribute["test"]
Message chains are also possible:
print delegate.aClassAttribute.aMethod( )
This calls aMethod
on
aClassAttribute
from all multiplex targets.
Behind the scenes, as a result of how Multiplex._ _getattr_ _
is coded, delegate.aClassAttribute
returns another Multiplex
object, as does the
.aMethod
(which collects bound methods into the
other anonymous Multiplex
). Finally, the special
method Multiplex._ _call_ _
enters the scene, and
Multiplex
delegates the call operation to each of
the bound methods, collecting their results into yet another
Multiplex
.
The design choice for Multiplex._ _nonzero_ _
is,
of course, quite debatable. As coded in the recipe, it makes a
Multiplex
true if all the registered objects are
true, including when there are no registered objects at all, which
may be a bit counterintuitive. Depending on your application, you
might therefore want to code this quite differently. Be sure to look
at Recipe 5.9 for a different approach to a
similar problem.
Recipe 5.8 and Recipe 5.9; documentation for the
operator
built-in module in the Library Reference.
Get Python Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.