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>
'''
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
Tri Noensie
  • 786
  • 7
  • 24

4 Answers4

11

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?-)

Community
  • 1
  • 1
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Guess I have to +1 by default since your name is in the question. – Kenan Banks Aug 19 '09 at 02:58
  • when you are struggling with *singleton* in python and this thread pops up automatically in suggested link only to make u realize there is *Monostate* as well....and u r like Jon Snow ([who knows nothing](https://www.quora.com/Why-does-Jon-Snow-know-nothing))......there is so much to learn!!! :) – NoobEditor Jan 07 '17 at 08:36
5
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.

Community
  • 1
  • 1
Dustin Getz
  • 21,282
  • 15
  • 82
  • 131
  • @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 Aug 19 '09 at 02:14
2

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
  • 11,870
  • 3
  • 23
  • 19
0

I believe the question was this:how does Borg._shared_state get updated? Answer: through self.__dict__ in every instance. self.__dict__ = self._shared_state gives you access to Borg._shared_state through self.__dict__ of every instance. When you change an instance attribute, you change that instance's __dict__ and since that instance's __dict__ is pointing to Borg._shared_state, you actually change Borg._shared_state, too. In python, when you say var1 = var2, var1 points to the value of var2 and it has access to that value. That's why var1 = a_different_value will actually change the value of var2 as well as the value of var1. I am also reading the above-mentioned book (online through BitBucket). BitBucket says that development stopped a few years ago, though. I wonder why that is. Maybe the author can comment on this. Thank you Alex Martelli for this great book.