Understanding the Borg Singleton Pattern in Python

I've seen this Borg Singleton Pattern code, but I couldn't figure out how new members added to a singleton object are added to the dictionary __shared_state = {}

.

Here is the Singleton code

class Borg(object):
    _shared_state = {}

    def __new__(cls,*args,**kwargs):
        obj = super(Borg,cls).__new__(cls,*args,**kwargs)
        obj.__dict__ = cls._shared_state
        return obj

class Child(Borg):
    pass

if __name__ == '__main__':
    borg = Borg()
    another_borg = Borg()

    print borg is another_borg
    child = Child()

    borg.only_one_var = "I'm the only one var"    
    print child.only_one_var

      

So my question is when an object is created borg.only_one_var

, how is it added to a dictionary_shared_state

+3


source to share


2 answers


By default, each instance gets its own dictionary, and therefore assigning an attribute to one instance does not affect other instances.

But you can make an instance dictionary to point to the new dict, and when you do this internally, it will be used from there to store the items.

In your case, every time an instance is created, you assign its dictionary to point to Borg. _shared_state

. Hence, all its instances will use the same dict to retrieve and set attributes.

This is mostly 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 a new dictionary __dict__

does not happen internally 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 introduction of keyword dictionaries in Python 3.3+, instance dictionaries of the same class can share some internal state to save space.

+3


source


In the simple case, from the context dictionary outside the class, as you probably know, you can add new key-value pairs, like so:

dic = {}
dic['first'] = 1
print(dic)
>>> {'first': 1}

      

In your case, you assigned a dictionary _shared_state

to an object dictionary obj.__dict__

. therefore it obj.__dict__

is now a reference to this dictionary _shared_state

.



When you use this dotted path notation on an instance obj

, you are actually adding a key-value pair to the dictionary and then to the dictionary _shared_state

.

Since this class is singleton, all dictionaries of other instances will refer to the same dictionary _shared_state

. therefore all instances will have "the same" __dict__

.

+1


source







All Articles