The Singleton design pattern (DP) has a catchy name, but the wrong focus -- on identity rather than on state. The Borg design pattern has all instances share state instead, and Python makes it, literally, a snap.
1 2 3 4 5 6 7 | class Borg:
__shared_state = {}
def __new__(cls, *args, **kwargs):
instance = super().__new__(cls, *args, **kwargs)
instance.__dict__ = cls.__shared_state
return instance
|
The 'Singleton' DP is all about ensuring that just one instance of a certain class is ever created. It has a catchy name and is thus enormously popular, but it's NOT a good idea -- it displays different sorts of problems in different object-models. What we should really WANT, typically, is to let as many instances be created as necessary, BUT all with shared state. Who cares about identity -- it's state (and behavior) we care about!
You can ensure this in many ways in Python, but the Borg design pattern is almost always best. Since the self.__dict__ of any instance can be re-bound, just re-bind it in __init__ to a class-attribute dictionary -- that's all! Now any reference or binding of an instance attribute will actually affect all instances equally -- "we all are one", and all that jazz. Thanks to David Ascher for suggesting the very appropriate name "Borg" for this DP.
Note that __getattr__ and __setattr__ are not involved -- they can be defined independently for whatever other purposes, or left undefined (and __setattr__, if defined, is NOT called for the rebinding of __dict__ itself). This only works with 'classic classes' (all classes in Python 2.1 and earlier, those that don't inherit from built-in types in 2.2 and later), whose instances keep all their per-instance state via self.__dict__ -- 2.2/+ classes with self.__slots__ do not support this idiom quite as smoothly (you can use getters/setters for such advanced-classes to deal with this issue, if you wish, but sticking to 'classic classes' for these needs may be simplest).
Download
Copy to clipboard
Very nice recipe (although the 2.0 version is much shorter).
I have 2 questions about it please:
*/ Why rebinding of self.__dict__ would not call self.__setattr__ ?
*/ I would like to inherit from Borg, while adding non-shared attributes in the derived class.
Thanks
A module can also act a singleton, without any code!
@s_h_a_i_o yes, calling self.__dict__ would call self.__setattr__ ... but, since is not defined, it works as expected. If you want a class, that lets you have, shared and private attributes, its even more simple to accomplish, take for example:
Here, setting attributes directly, will be added as private for each instance. if you need some shared attribute use getshared/setshared instead
@Jonathan yes, you're completely right about it, but I think it's usefull(in order to learn) to play with the language a little bit, although, there are times that maybe this pattern would be more appropiate if you want for example, keep a shared object thread-safe