Using Singletons in Python
What is a singleton?
A singleton is an object of which there is only one, semantically, in memory.
Builtin singletons
None
is a frequently used singleton. NotImplemented
is another, much less used. We can test for singleton identity:
>>> maybe_None = None
And then
maybe_None is None
returns True
.
Creating a semantic singleton
You can just use an object instance to create one that needs no functionality in a module's global namespace (all caps, because it's a global constant):
SINGLETON = object()
Be careful not to overwrite the object on the global namespace. If you give it a good name, it should be obvious when someone is doing something wrong with it.
This is similar to the example you gave. However, I wouldn't do what you're doing if creating the object is expensive - it makes it so that it's expensive to import the module, even if you don't want to use the object.
Why you wouldn't want a global singleton on import
Where you would not want it to have a singleton created on import would be any time you want to inexpensively import the module.
You want your modules to be inexpensive to import when dynamically generating documentation, for example.
Another case where you should prefer inexpensive imports are for running unittests.
Also, the method you use does not support inheritance.
So I'm going to show you some ways to work around this.
A singleton instance
Where you only want to have one instance of a class, you can do this in various ways:
customize __new__
and use a class attribute, instance
class Controller(object): instance = None def __new__(cls): if cls.instance is not None: return cls.instance else: inst = cls.instance = super().__new__(cls) return inst
And note that this supports inheritance.
memoize a factory function
You can create a function attribute to memoize the function
def get_controller(): if hasattr(get_controller, 'instance'): return get_controller.instance else: inst = get_controller.instance = Controller() return inst
You could also use the module or class namespace to keep the instance in - since the function and the module should both be singletons as well.
Or use lru_cache (since get_controller takes no arguments) to memoize it.
from functools import lru_cache @lru_cache def get_controller(): return Controller()
Note that if you add arguments to get_controller
, lru_cache will return a singleton for each unique combination of arguments, up to its cache size (theoretically), after which it forgets instances, so be careful with this if you add arguments - probably better to implement your own behavior, as I did with the function attribute example.
This can support inheritance, for example.
@lru_cache def get_controller(controller_type): if issubclass(controller_type, Controller): return controller_type() else: raise TypeError('must supply a Controller type')