Why Is the Borg Pattern Better Than the Singleton Pattern in Python

Why is the Borg pattern better than the Singleton pattern in Python

The real reason that borg is different comes down to subclassing.

If you subclass a borg, the subclass' objects have the same state as their parents classes objects, unless you explicitly override the shared state in that subclass. Each subclass of the singleton pattern has its own state and therefore will produce different objects.

Also in the singleton pattern the objects are actually the same, not just the state (even though the state is the only thing that really matters).

Can someone explain me the Borg design pattern in python?

First, class variable are shared by all instances.

class Spam:
ham = 'ham'

s1 = Spam()
s2 = Spam()

print(s1.ham) # => ham
print(s2.ham) # => ham

Spam.ham = 'egg'

print(s1.ham) # => egg
print(s2.ham) # => egg

Second, attrbutes are managed by self.__dict__.

class Spam:
pass

s1 = Spam()
s1.__dict__['ham'] = 'ham'
print(s1.ham) # => ham

Borg pattern uses this features. Borg.__shared_dict is class variable.
This behavior occurs because _shared_dict is assigned to self.__dict__ whenever an instance of Singleton is created.

Understanding Borg Singleton Pattern in Python

By default each instance gets its own dictionary and hence assigning an attribute to one instance doesn't affect other instances.

But you can make an instance's dictionary to point to a new dict and when you do so internally it will be used from there on to store items.

In your case every time an instance is being created you're assigning its dictionary to point to a Borg. _shared_state. Hence, all its instances will use the same dict to fetch and set attributes.

It's basically equivalent to:

shared = {}

class A(object):
def __init__(self):
self.__dict__ = shared

Demo:

>>> ins = [A() for _ in range(5)]
>>> ins[0].x = 100
>>> for i in ins:
... print(i.x)
...
100
100
100
100
100

>>> shared
{'x': 100}

In CPython the assignment of new dictionary to __dict__ happens inside PyObject_GenericSetDict:

int
PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
{
PyObject **dictptr = _PyObject_GetDictPtr(obj);
...
if (!PyDict_Check(value)) {
PyErr_Format(PyExc_TypeError,
"__dict__ must be set to a dictionary, "
"not a '%.200s'", Py_TYPE(value)->tp_name);
return -1;
}
Py_INCREF(value);
Py_XSETREF(*dictptr, value); # Set the dict to point to new dict
return 0;
}

Note that since the arrival of key-sharing dictionaries in Python 3.3+ the dictionaries of instances of same class can share some internal state to save space.

What is a singleton/borg pattern? Why don't they work for me/where is my concept wrong?

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 # []

Borg Design Pattern

Actually, this is not the recommended approach, and I have never seen it used in real code.

The recommended approach is to use a module as a module already has a "global" namespace (see this answer for more info on globals(), locals(), and vars()).


However, in the interests of understanding:

What you have so far is only a basic framework for shared state at the instance level. What you need now is the rest of the state you want to track:

class Config(Borg):

def __init__(self, config_file):
super(Config, self).__init__()
# load and parse file, saving settings to `self`

One disadvantage to this method is that you can have several instances consuming memory that all know the same thing. (Not much memory, true.)

Another method of accomplishing "shared state" is to only create one instance, and then have the class always return that same instance -- otherwise known as a singleton.

class Config(object):

the_one = None

def __new__(cls, config):
if cls.the_one is None:
cls.the_one = Super(Config, cls).__new__(cls)
# load and parse file, saving settings to `cls.the_one`
return cls.the_one

Either method would result in the following:

>>> config = Config('my_config_file.cfg')
>>> config.screen_size
# whatever was saved during the loading and parsing of the config file
# for 'screen_size'

Why is this Python Borg / Singleton pattern working

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.

Borg pattern or just module with functions

A singleton and a module represent only one instance, for all the application lifetime. This instance is kept instantiated, even when it's not needed.

The borg pattern is about shared state. Each client class will create a new instance of the borg, but that instance will be disposed when it's not needed anymore - it is a much more elegant approach.

Beyond that, it is much easier to subclass or mock a borg.

Python Borg design pattern, running method on creation of class

I think your solution isn't that bad with the caveat being that you have absolutely have to populate that __dict__ before next init is called again or firstInitializationMethod() will be called more than once. It's guaranteed to happen in your example because of self.val = arg.

However if your Simpleton would do no assignments inside class instance namespace in its __init__ call then your solution could fail.

More straightforward and robust way is just to use class attribute like this:

class Singleton(Borg):
_first_initialization = True
def __init__(self,arg):
Borg.__init__(self)
if Singleton._first_initialization:
firstInitializationMethod()
Singleton._first_initialization = False

You can test this code by just replacing firstInitializationMethod() with print and creating some Simpleton objects to see that it'll only get called once.

This works and _first_initialization isn't overwritten back to True on every __init__ call because class namespace is separate from class instance namespace and Borg is only affecting the latter(namely making all instances of Simpleton to use the same __dict__).

Followup question:
I tried the code with self instead of Singleton and it still worked. It seems they resolve to the same thing. Is there a reason to use Singleton?

Consider this code using these two approaches where SingletonSelfless is the one using Singleton._first_initialization, tinker() is just returning self.__first_initialization:

a = Singleton('a')
print(a)
b = Singleton('b')
print(a,b)
c = Singleton('c')
print(a,b,c)
print(Singleton._first_initialization, a.tinker(),b.tinker(),c.tinker())

a = SingletonSelfless('a')
print(a)
b = SingletonSelfless('b')
print(a,b)
c = SingletonSelfless('c')
print(a,b,c)
print(SingletonSelfless._first_initialization, a.tinker(),b.tinker(),c.tinker())

and its output:

doing some init!!
a
b b
c c c
True False False False
doing some init!!
a
b b
c c c
False False False False

From practical point of view both implemenations work like we wanted them to yet there is clear difference with _first_initialization variable(s) values.

Answer is quite simple.
Even though class namespace and class instance namespace are separate, the instance still can access the class namespace.
But it only does as fallback - class instance namespace has absolute priority - but when it can't find name in its own instance namepsace then it tries to use class one.
So let's look at __init__ in this Singleton:

class Singleton(Borg):
_first_initialization = True
def __init__(self,arg):
Borg.__init__(self)
if self._first_initialization:
print('doing some init!!')
self._first_initialization = False
self.val = arg
def tinker(self):
return self._first_initialization
def __str__(self): return self.val

Even though the instance doesn't have _first_initialization our if is being resolved using Singleton._first_initialization.
However setting self._first_initialization to False creates the _first_initialization variable in instance namespace.
Thanks too Borg all our instances share the same __dict__, so on subsequent init calls there will be a _first_initialization in class instance namespace(the one created at first __init__ call with value False)
and our conditional statement will resolve as we'd want it to - not doing another firstInitializationMethod() (here print for demonstration purpouses).

However our original _first_initialization residing in class namespace is unchanged. That's why we get True False False False.

In SingletonSelfless, we are never creating _first_initialization in class instance(s) so tinker() call will fallback to class namespace. That's why there are 4 falses - all calls points to the same object(SingletonSelfless._first_initialization bool variable).

While in Singleton we have two different objects - one from class namespace and the other in class instance namespace shared between instances.
So why use Singleton. instead of self. ? Well for the starters with the first we 'save' incredibly tiny bit of memory by having only one _first_initialization bool in there!
But the real reason is that it's harder to accidently change variable hiding in class namespace.
If we are using self._first_initialization and somewhere later in our code something like this happened for whatever reason(or the _shared_dict from Borg would be cleared or changed affecting that would reside there):
a._first_initialization = 'Lol' or in Singleton or its child method self._first_initialization = 'ROFL'
then we gonna have some serious problems doing init of new Singleton objects.
With Singleton._first_initialization it'd be fine since the variable used for init could be only modified by explicit Singleton._first_initialization='bad idea'

Python Singleton design pattern tracking application stats

Whether singletons are "bad" or not seems to be a matter of taste. Certainly they have their place, and any of the variations on the singleton theme should work for you.

The "Borg pattern" (less colorfully & much less commonly, "StatelessProxy" or "Monostate") has been a popular Python alternative to Singleton probably ever since Alex Martelli's clever ActiveState recipe Singleton? We don't need no stinkin' singleton: the Borg design pattern. It differs from Singleton in allowing multiple distinct objects of a class, all sharing common data. The Singleton pattern, by contrast, ensures that only one instance of a class is ever created.

A discussion of the Borg vs Singleton issue can be found in this stackoverflow post: Why is the Borg pattern better than the Singleton pattern in Python. The implementations at the top of the post might be puzzling due to the missing _init_default_register method, whose purpose is to create and initialize the common data attributes, once only. For reference and comparison, here are complete implementations (in Python 3), both of which create a single data attribute (a dict named data):

The standard way to implement Singleton in Python is with a metaclass;

class Singleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)

def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance

You would make your statistics-tracking class a singleton by giving it this metaclass:

class StatsTrackerSingleton(metaclass=Singleton):
def __init__(self):
self.data = {}

# ... methods to update data, summarize it, etc. ...

The Borg pattern is simpler to implement and, because it doesn't use a metaclass, potentially more flexible:

class StatsTrackerBorg():
__shared_data = {'data':{}}
# RHS guarantees a common dict named `data`

def __init__(self):
"""Make every instance use StatsTrackerBorg.__shared_data
for its attribute dict."""
self.__dict__ = self.__shared_data

# ... methods to update data, summarize it, etc. ...

With both patterns, as implemented above, you can use the common dict data, and you can simply get and set shared attributes using the dot operator. For example, using the Borg class:

>>> a = StatsTrackerBorg()
>>> b = StatsTrackerBorg()
>>> a is b # would be True for StatsTrackerSingleton
False
>>> vars(a) is vars(b)
True

>>> vars(a)
{'data': {}}
>>> a.data['running_time'] = 10000
>>> b.bar = 10
>>> vars(a)
{'data': {'running_time': 10000}, 'bar': 10}

>>> b.foo = 'y'
>>> a.foo
'y'

A noteworthy difference between the two patterns: a subclass of a "Borg'ed" class shares the same common state with the superclass, and can still add more shared state accessible to its instances, whereas each subclass of a Singleton class gets its own unique instance and thus its own common state, disjoint from that of the superclass. For some intended applications, one of these behaviors may be clearly more appropriate than the other.

Singleton/Borg pattern based on different parameters passed while creating the object

No, what you use is what I'd use; use a dictionary for the shared states.

You can simplify it slightly by using the return value of dict.setdefault() rather than ignore it:

def __init__(self, *args, **kwargs):
context_key = hash('{0}{1}'.format(cPickle.dumps(args),cPickle.dumps(kwargs)))
self.__dict__ = self.__shared_state.setdefault(context_key, {})

All this can be encapsulated in a metatype:

class PerArgsBorgMeta(type):
def __new__(mcls, name, bases, attrs):
cls = super(PerArgsBorgMeta, mcls).__new__(mcls, name, bases, attrs)
setattr(cls, '_{}__shared_state'.format(name), {})
return cls

def __call__(cls, *args, **kwargs):
instance = super(PerArgsBorgMeta, cls).__call__(*args, **kwargs)
context_key = hash('{0}{1}'.format(cPickle.dumps(args),cPickle.dumps(kwargs)))
state = getattr(cls, '_{}__shared_state'.format(cls.__name__))
instance.__dict__ = state.setdefault(context_key, {})
return instance

Then use this as a __metaclass__ attribute on the class:

class SomeBorgClass:
__metaclass__ = PerArgsBorgMeta

Do note that using hash(cPickle.dumps(kwargs)) will still create distinct hashes for dictionaries with collisions:

>>> import cPickle
>>> hash(cPickle.dumps({'a': 42, 'i': 81}))
-7392919546006502834
>>> hash(cPickle.dumps({'i': 81, 'a': 42}))
2932616521978949826

The same applies to sets. Sorting (recursively if you must be exhaustive) can help here, but be careful that you don't then produce false-positives between, say, a set passed in as a value, and a tuple with the same values in it used instead. There are increasingly convoluted work-arounds possible for each of these, but at some point you just have to accept the limitation rather than complicate the hashing code more still.



Related Topics



Leave a reply



Submit