1

Similar questions has been asked many times, but I have problems understanding it. I was thinking that the Singleton or Borg pattern can be used to create only one instance of an object, or to share its state. I have a (working) test example which does not work as I expect. Either the code is incorrect, or I misunderstand the concept of singleton/borg patterns.

I used a singleton and a borg pattern to create the following code in the file borg.py:

class Singleton(object):
  _instance = None
  def __new__(class_, *args, **kwargs):
    if not isinstance(class_._instance, class_):
        class_._instance = object.__new__(class_, *args, **kwargs)
    return class_._instance



class MySingleton(Singleton):

    def __init__(self):
        self._list = []

    def add(self,x):
        self._list.append(x)

    def get(self):
        return self._list

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

    def add(self,x):
        self._list.append(x)

    def get(self):
        return self._list

then a file module.py

from borg import MyBorg
myborg = MyBorg()
myborg.add(42)
print "just added something"

and finally the main code:

from borg import MyBorg
import module
myborg = MyBorg()
myborg.add(4711)
print myborg.get()

One should replace MyBorg by MySingleton in the latter two classes to use the Singleton instead of the borg.

Now, when I run the main code I clearly can see that modules.py is called first, adding a value to the list. After that, the Singleton/Borg pattern is also instantiated in the main code, and (another) value is added. I expected to have two values in the list (42 and 4711), instead I only have the latter value in the list.

It might be the case that the instance in module.py went out of scope and so whatever were done in module.py has been deleted. But what I need is some was of having an object to contain the same content, no matter where I use it.

How can I achieve this? How can I make sure, that when I create an instance of the object MyBorg (or whatever), it contains the value '42' in the list, as added in module.py? What pattern/mechanism should I use to achieve this?

Community
  • 1
  • 1
Alex
  • 41,580
  • 88
  • 260
  • 469

1 Answers1

2

The reason for the behavior you're seeing is that in both cases __init__ gets called every time you do instance = WhateverClass().

Note that you are passing the same instance around. However, that instance is getting it's _list attribute cleared in __init__.

class Singleton(object):
    _instance = None
    def __new__(class_, *args, **kwargs):
        if not isinstance(class_._instance, class_):
            class_._instance = object.__new__(class_, *args, **kwargs)
            return class_._instance

class Foo(Singleton):
    def __init__(self):
        self.data = []
    pass

a = Foo()
a.data.append('Never see this')
b = Foo()
print a is b  #True
print a.data  # []
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Hmm good point. But where to set it to an empty list? I need to find out when the first instance is created? Or hardcode it in the `__new__` method of the Singleton? – Alex Mar 21 '13 at 15:44
  • Would it be good practice to adjust e.g. the borg pattern and write `__shared_state = {'_list':[]}` instead? I.e. to initialize what I need in the shared-state dictionary? – Alex Mar 21 '13 at 15:48
  • 2
    I don't generally consider singleton's to be a great thing anyway... modules make decent singletons in some cases (Though you can't subclass that). Another thing that you could do is add an `initialized` variable to your Singleton API. You'd set `class_._intstance.initialized = False` in `__new__`. Then `__init__` in the subclass is responsible for setting `self.initialized` to `True` and not executing if `self.initialized` is `True`. – mgilson Mar 21 '13 at 15:50