Need help subclassing Borg

Bengt Richter bokr at oz.net
Sat May 7 12:48:27 EDT 2005


On Sat, 07 May 2005 22:28:34 +1000, Steven D'Aprano <steve at REMOVETHIScyber.com.au> wrote:

>I've been working with the Borg design pattern from here:
>http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531
>
>and I'm having problems subclassing it.
>
>I'm a newbie, so I've probably missed something obvious.
>
>I want two Borg-like classes where all instances share state within each
>class, but not between them. This is what I tried:
>
>py> class Borg:
>py>     _shared_state = {}
>py>     def __init__(self):
>py>         self.__dict__ = self._shared_state
>py> 
>py> class Duck(Borg):
>py>     def __init__(self):
>py>         Borg.__init__(self)
>py>         self.covering = "feathers" # all ducks are feathered
>py>
>py> class Rabbit(Borg):
>py>     def __init__(self):
>py>         Borg.__init__(self)
>py>         self.covering = "fur" # all rabbits are furry
>py>
>py> bugs = Bunny(); daffy = Duck()
>py> daffy.covering
>'feathers'
>py> bugs.covering
>'feathers'
>
>Not what I wanted or expected. What I wanted was for the subclasses Rabbit
>and Duck to each inherit Borg-like behaviour, but not to share state with
>any other Borgs. In other words, all Ducks share state, and all Rabbits
>share state, but Ducks and Rabbits do not share state with each other.
>
>I now see why Ducks and Rabbits are sharing state: they both share the
>same __dict__ as all Borg instances. But I don't see how to get the
>behaviour I want. (Except by cutting and pasting the Borg code into each
>one.) Can anyone help?
>

If you are using old-style classes (which you need for this Borg), then you could
keep different shared state dicts within a _shared_states dict, e.g. based on
the name of the subclassed classes, e.g.,

 >>> class Borg:
 ...     _shared_states = {} #note plural
 ...     def __init__(self):
 ...         self.__dict__ = self._shared_states.setdefault(self.__class__.__name__, {})
 ...
 >>> class Duck(Borg):
 ...     def __init__(self):
 ...         Borg.__init__(self)
 ...         self.covering = "feathers" # all ducks are feathered
 ...
 >>> class Rabbit(Borg):
 ...     def __init__(self):
 ...         Borg.__init__(self)
 ...         self.covering = "fur" # all rabbits are furry
 ...
 >>> bugs = Bunny(); daffy = Duck()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 NameError: name 'Bunny' is not defined

Hm, where'd that Bunny come from?

 >>> bugs = Rabbit(); daffy = Duck()
 >>> daffy.covering
 'feathers'
 >>> bugs.covering
 'fur'
 >>> vars(Borg)
 {'__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x02EE8D14>, '_sha
 red_states': {'Rabbit': {'covering': 'fur'}, 'Duck': {'covering': 'feathers'}}}
 >>> donald = Duck()
 >>> donald.covering
 'feathers'
 >>> roger = Rabbit()
 >>> roger.covering
 'fur'
 >>> Borg._shared_states['Duck']
 {'covering': 'feathers'}
 >>> Borg._shared_states['Rabbit']
 {'covering': 'fur'}

Since you are calling Borg.__init__(self), you could specify some other
classifier than the implicit class name, e.g., Borg.__init__(self, 'feathered')
vs Borg.__init__(self, 'furred') and use that as the key in the setdefault call.

As mentioned in the recipe discussion, new style classes differ somewhat, but you
can accomplish the same functionality, just that if you have special things like
__slots__ or descriptors, you may have to think about how they might interact with
your shared state access.

Regards,
Bengt Richter



More information about the Python-list mailing list