Implementing the Singleton Pattern in Python: Modules, Decorators, Classes, __new__ Method, and Metaclasses
This article explains the Singleton design pattern in Python, describing why a single instance may be needed, and demonstrates five implementation techniques—including module-level singletons, decorators, class methods, the __new__ method, and metaclasses—while addressing thread‑safety concerns with locking mechanisms.
The Singleton pattern ensures that a class has only one instance throughout a program, which is useful for shared resources such as configuration objects.
In Python, several ways exist to implement a singleton:
Using a module (modules are naturally singletons because they are loaded only once).
Using a decorator that caches the created instance.
Using a class with a class‑level method that stores the instance.
Using the __new__ method to control object creation.
Using a metaclass to enforce a single instance.
Module‑based singleton
<code># mysingleton.py
class Singleton(object):
def foo(self):
pass
singleton = Singleton()
</code>Import the object directly:
<code>from mysingleton import singleton
</code>Decorator‑based singleton
<code>def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class A(object):
a = 1
def __init__(self, x=0):
self.x = x
a1 = A(2)
a2 = A(3)
</code>Class‑method singleton (thread‑unsafe)
<code>class Singleton(object):
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
</code>When used in multithreaded code, this version can create multiple instances because the check and creation are not atomic.
Thread‑safe class‑method singleton
<code>import time, threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
time.sleep(1)
@classmethod
def instance(cls, *args, **kwargs):
with cls._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = cls(*args, **kwargs)
return cls._instance
</code>All threads obtain the same object, and the lock guarantees safety during initialization.
Singleton via __new__
<code>class Singleton(object):
_instance_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with cls._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = object.__new__(cls)
return cls._instance
</code>Instances can be created with the usual obj = Singleton() syntax while still ensuring a single object.
Metaclass‑based singleton
<code>class SingletonType(type):
_instance_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with cls._instance_lock:
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
return cls._instance
class Foo(metaclass=SingletonType):
def __init__(self, name):
self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
# obj1 and obj2 refer to the same instance
</code>Metaclasses give fine‑grained control over class creation and are a clean way to enforce the singleton property.
Overall, the article provides complete Python code examples for each approach and highlights the importance of locking when singletons are accessed from multiple threads.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.