5.21 Delegating Messages to Multiple Objects


Credit: Eduard Hiti

5.21.1 Problem

You need to multiplex messages (attribute requests) to several objects that share the same interface.

5.21.2 Solution

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"

5.21.3 Discussion

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.

5.21.4 See Also

Recipe 5.8 and Recipe 5.9; documentation for the operator built-in module in the Library Reference.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2005
Pages: 346

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net