2

i just stumbled around the net and found these interesting code snipped:

http://code.activestate.com/recipes/66531/

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
    # and whatever else you want in your class -- that's all!

I understand what a singleton is but i don't understand that particular code snipped. Could you explain me how/where "__shared_state" is even changed at all?

I tried it in ipython:

In [1]: class Borg:
   ...:         __shared_state = {}
   ...:     def __init__(self):
   ...:             self.__dict__ = self.__shared_state
   ...:     # and whatever else you want in your class -- that's all!
   ...: 
In [2]: b1 = Borg()
In [3]: b2 = Borg()
In [4]: b1.foo="123"
In [5]: b2.foo
Out[5]: '123'
In [6]: 

but cannot fully understand how this could happen.

enthus1ast
  • 2,099
  • 15
  • 22
  • This version doesn't work with new-style classes -- see http://stackoverflow.com/questions/8992241/borg-design-with-new-style-classes for discussion. – agf Aug 26 '12 at 06:48
  • Also, it's bad style because `id(b1)` will be different from `id(b2)`, and `b1 is b2` will give `False`. A simple and solid way that works with both old and new style classes is by declaring a normal class `Foo` and then right after the class definition doing `Foo = Foo()`. – Erik Kaplun Aug 26 '12 at 13:29

3 Answers3

12

Because the class's instance's __dict__ is set equal to the __share_state dict. They point to the same object. (Classname.__dict__ holds all of the class attributes)

When you do:

b1.foo = "123"

You're modifying the dict that both b1.__dict__ and Borg.__shared_state refer to.

Joel Cornett
  • 24,192
  • 9
  • 66
  • 88
  • Incidentally, can anyone explain *why* you would want to use a pattern like this? I would think that in most situations (in every situation I can think of) you would just hold multiple references to the *same* instance, without all this hackery. – Joel Cornett Aug 26 '12 at 06:07
  • 1
    It's short, it does what i want and i dont understand it at first. Now i get it, when i equals a dic i only copy an reference, this was not clear to me. Thank you for this Lesson! – enthus1ast Aug 26 '12 at 06:15
  • @DavidosKrausos: No problem. No judgement on my part, I'm just genuinely curious what sort of thing you would use this pattern for. – Joel Cornett Aug 26 '12 at 06:18
  • 1
    @DavidosKrausos: Actually, now I get it. The answer was in [this Question](http://stackoverflow.com/questions/1318406/why-is-the-borg-pattern-better-than-the-singleton-pattern-in-python). – Joel Cornett Aug 26 '12 at 06:20
  • 1
    It's not the class's `__dict__` but the instance's `__dict__`'s which are set equal to the `__shared_state` dict. – Don O'Donnell Aug 26 '12 at 06:21
3

The __init__ method, which is called after instantiating any object, replaces the __dict__ attribute of the newly created object with the class attribute __shared_state.

a.__dict__, b.__dict__ and Borg._Borg__shared_state are all the same object. Note that we have to add the implicit prefix _Borg when accessing private attribute from outside the class.

In [89]: a.__dict__ is b.__dict__ is Borg._Borg__shared_state
Out[89]: True
Mohammad Alhashash
  • 1,543
  • 1
  • 14
  • 30
3

The instances are separate objects, but by setting their __dict__ attributes to the same value, the instances have the same attribute dictionary. Python uses the attribute dictionary to store all attributes on an object, so in effect the two instances will behave the same way because every change to their attributes is made to the shared attribute dictionary.

However, the objects will still compare unequal if using is to test equality (shallow equality), since they are still distinct instances (much like individual Borg drones, which share their thoughts but are physically distinct).

nneonneo
  • 171,345
  • 36
  • 312
  • 383