tags:

views:

319

answers:

3

I've been reading Thinking in python by Bruce Eckel. Currently, I'm reading the Pattern Concept chapter. In this chapter, Eckel shows the different implementations of Singletons in python. But I have an unclear understanding of Alex Martelli's code of Singleton (utilizing inheritance, instead of privated nested class).

This is my understanding of the code so far:

  • All Singleton objects are subclasses of Borg
  • *_shared_state* is initially an empty dictionary
  • *_shared_state* is a global variable; Any objects utilizing Borg will have the same *_shared_state* value

My confusion so far:

  • What's the purpose of this line: self.__dict__ = self._shared_state ; or the purpose of the dictionary
  • How did all the objects of Singletons eventually have the same val, even though they are all different instances of the class.
  • In overall, I don't know how Borg works

Many Many thanks in advance!

-Tri

*Update: What is stored in *_shared_state* after each Singleton object creation?

#: Alex' Martelli's Singleton in Python
class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state

class Singleton(Borg):
  def __init__(self, arg):
    Borg.__init__(self)
    self.val = arg
  def __str__(self): return self.val

x = Singleton('sausage')
print x
y = Singleton('eggs')
print y
z = Singleton('spam')
print z
print x
print y
print ´x´
print ´y´
print ´z´
output = '''
sausage
eggs
spam
spam
spam
<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>
'''
+5  A: 
class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state

1) since Borg._shared_state is initialized at class-level (not in __init__) it is effectively static, e.g. shared by all instances of the class.

2) self.__dict__ is a dictionary that all objects have; it contains all instance attributes. thus,

self.a=1
assert(self.a == self.__dict__['a']) #True

3) Note that a Borg means 'all instances share the same state', a singleton means 'only one instance'. It's pretty much the same effect. Alex Martelli pointed out that Borg is a pythonic Monostate, so see Monostate vs Singleton on SO.

It seems that his Singleton class is more aptly named

class ThisClassHasSingletonBehavior(Borg):
    ....

because

<__main__.Singleton instance at 0079EF2C>
<__main__.Singleton instance at 0079E10C>
<__main__.Singleton instance at 00798F9C>

proves that x,y,z are not the same instance, even though they share the same state. So it's not REALLY a singleton, it just has the same overall effect, and is easy. AFAICT Borg is a elegant python-pattern for shared state (same state) behavior, where Singleton is an elegant cpp-pattern for same. not that this behavior is ever elegant.

Dustin Getz
http://code.activestate.com/recipes/66531/
Dustin Getz
@Dustin, heh, I agree it's not a Singleton -- "we don't need no stinkin' singleton" and followup heated debate in the comments;-). Wow, this really takes me back down Memory Lane...!-)
Alex Martelli
+2  A: 

Comparing to instances of a normal class may help you see what they're doing:

>>> class NormalClass:
       def __init__(self, arg):
           self.x = arg


>>> a = NormalClass(1)
>>> b = NormalClass(2)
>>> a.x
1
>>> a.__dict__
{'x': 1}
>>> b.x
2
>>> b.__dict__
{'x': 2}

Note how the instances here each have their own unshared __dict__ where
their own x attribute is stored.

Editing to add: Now, let's make these normal objects share a dictionary and see what happens...

>>> Another_Dictionary_We_Make = {}
>>> Another_Dictionary_We_Make['x'] = 1000
>>> Another_Dictionary_We_Make
{'x': 1000}
>>> a.__dict__ = Another_Dictionary_We_Make
>>> a.x
1000
>>> b.x
2
>>> b.__dict__ = Another_Dictionary_We_Make
>>> b.x
1000
>>> b.x = 777
>>> b.__dict__
{'x': 777}
>>> a.x
777
>>> a.__dict__
{'x': 777}

This shows making the dict be the same for both objects after they're created. The Borg/Singleton is simply causing instances to share the same dictionary by initializing them to use the same dict when they're created.

Anon
+5  A: 

Good answers so far, but let me also answer directly... self.__dict__ holds the attributes (i.e., the state) of instance self (unless its class does peculiar things such as defining __slots__;-). So by ensuring that all instances have the same __dict__ we're ensuring they all have the same attributes, i.e., exactly the same state. So, Borg is a neat Python implementation of the general Monostate pattern, but without any of the downsides that Robert Martin identified in his essay on Monostate in C++.

See also this SO thread for Monostate vs Singleton -- of course much of the discussion is NOT relevant to Python (obviously in Python Borg does not stand in the way of inheritance, on the contrary!, but then Singleton can be quite as transparent thanks to e.g. __new__, so the tradeoff are quite different...).

The funniest thing? In the 8+ years since I first conceived of Borg (before David Ascher, now CEO of Mozilla Messaging, suggested the cool Borg name) I've had occasion to use any kind of singleton or monostate maybe four times in all -- and three times out of the four I soon refactored it out in favor of a more flexible approach!-) (The fourth time was a subsystem that didn't prove very successful and didn't get much followup work/maintenance;-).

Second-funniest thing is that Guido, personally, detests Borg;-). Not that he has any real liking for Singleton either: he thinks in Python "singletonarity", if needed, should be done as a module (or, I'd like to add, a class instance masquerading as a module -- see e.g. my observation in section 7.2.6 of Python in a Nutshell, e.g. in this pirate copy;-). But, Borg appears to particularly offend his design aesthetics!-) Peculiar, since he did nominate me for PSF membership based on my Five Easy Pieces essay, which is basically ALL about Borg (and variants thereof, and considerations thereupon)...!-). Ah well, guess he can "hate the sin but love the sinner", hm?-)

Alex Martelli
Guess I have to +1 by default since your name is in the question.
Triptych
holy, i <3 stackoverflow
Dustin Getz
Thank you for the man himself to make the appearance!!
Tri