Python Singleton Implementation – 5 Best Methods Compared
When writing Python applications, especially for logging, configuration handling, or caching, the Singleton pattern can be useful to ensure that only one instance of a class exists throughout the program.
Python offers several ways to implement a singleton. In this blog post, we’ll explore five of the most Pythonic and practical approaches — decorator, base class, metaclass, enhanced decorator, and module-based singleton — and compare their pros and cons.
Let’s break them down!
🔁 Method 1: Singleton Decorator (Basic)
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
@singleton
class MyClass:
pass
✅ Pros:
- Clean and intuitive usage
- Easy to apply on any class
❌ Cons:
- The decorated class becomes a function (not a type)
- Class methods cannot be accessed via the class directly
- Can cause confusion when using
type()
or subclassing
📦 Method 2: Singleton as a Base Class
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
class MyClass(Singleton):
pass
✅ Pros:
- True class, supports normal instantiation and inheritance
- Clear and readable
❌ Cons:
- Involves multiple inheritance
- Can get complex if base class overrides
__new__
🧬 Method 3: Singleton via Metaclass (Recommended)
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class MyClass(metaclass=Singleton):
pass
✅ Pros:
- Elegant and Pythonic
- Works with inheritance automatically
- No clutter in the class body
❌ Cons:
- Slightly advanced concept (metaclass usage)
💡 This is the most scalable and clean method. Ideal for frameworks and libraries.
🧙♂️ Method 4: Advanced Decorator with Wrapped Class
def singleton(class_):
class Wrapped(class_):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls, *args, **kwargs)
cls._instance._sealed = False
return cls._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super().__init__(*args, **kwargs)
self._sealed = True
Wrapped.__name__ = class_.__name__
return Wrapped
✅ Pros:
- Looks like a regular class
- Supports inheritance and class-level behavior
❌ Cons:
- Adds complexity
- Recursive
super()
issues in some edge cases - Memory overhead due to inner class creation
📁 Method 5: Module-Level Singleton
singleton_logger.py
:
class Logger:
def log(self, message):
print(message)
logger = Logger()
Usage in other modules:
from singleton_logger import logger
logger.log("This is a log message.")
✅ Pros:
- Simple and effective
- Python’s import system guarantees singleton behavior
❌ Cons:
- Not lazily instantiated
- Limited flexibility
🧠 Conclusion: What’s the Most Pythonic Singleton?
The metaclass-based Singleton is often considered the most Pythonic, especially when working on large applications or frameworks. It cleanly enforces the singleton constraint while preserving all class-level features.
If you prefer simplicity and readability, decorators or module-level singletons are great too — just be aware of their limitations.