Question
How to Implement a Singleton in Python: Patterns, Tradeoffs, and Pythonic Alternatives
Question
I have several classes that I might want to behave as singletons, for example a logger, although the exact use case is not the main point. I would prefer a solution that does not require repetitive boilerplate inside every class if inheritance or decoration can handle it.
I am comparing several possible approaches for implementing a singleton in Python and want to understand their tradeoffs.
Method 1: A decorator
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(BaseClass):
pass
Pros
- Decorators are additive and often feel more intuitive than multiple inheritance.
Cons
- Objects created through
MyClass()are singleton instances, butMyClassitself becomes a function rather than a class. - That means class methods and class-based introspection no longer behave normally.
For example:
x = MyClass()
y = MyClass()
t = type(x)
Here, x is y, but MyClass is no longer the original class object.
Method 2: A base class
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not isinstance(cls._instance, cls):
cls._instance = object.__new__(cls)
return cls._instance
class MyClass(Singleton, BaseClass):
pass
Pros
MyClassremains a real class.
Cons
- Requires multiple inheritance.
__new__may become harder to reason about when combined with other base classes.
Method 3: A metaclass
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]
# Python 2
class MyClass(BaseClass):
__metaclass__ = Singleton
# Python 3
class MyClass(BaseClass, metaclass=Singleton):
pass
Pros
MyClassremains a real class.- The behavior naturally applies per class.
- Uses the metaclass mechanism directly.
Cons
- Are there any important downsides?
Method 4: A decorator that returns a subclass with the same name
def singleton(class_):
class class_w(class_):
_instance = None
def __new__(cls, *args, **kwargs):
if class_w._instance is None:
class_w._instance = super(class_w, cls).__new__(cls)
class_w._instance._sealed = False
return class_w._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super(class_w, self).__init__(*args, **kwargs)
self._sealed = True
class_w.__name__ = class_.__name__
return class_w
@singleton
class MyClass(BaseClass):
pass
Pros
MyClassremains a real class.- Inheritance is handled automatically.
Cons
- It creates a wrapper subclass for every singleton class.
- The
_sealedattribute adds extra complexity. - It can interfere with
super()and customization of__new__or__init__.
Method 5: A module
Use a module file such as singleton.py and keep shared state there.
Pros
- Very simple.
Cons
- Not lazily instantiated in the same class-based way.
I am not looking for a debate about whether the singleton pattern is good or bad. Instead, I want to understand which implementation style is most Pythonic, especially in the sense of following the principle of least astonishment.
Short Answer
By the end of this page, you will understand what a singleton is in Python, how modules, __new__, decorators, and metaclasses can be used to implement it, and why Python developers often prefer simpler alternatives. You will also see the practical tradeoffs between a "true singleton class" and a module-level shared object.
Concept
A singleton is a pattern that ensures only one instance of something exists during a program's lifetime, while providing a global way to access it.
In Python, this idea appears often in things like:
- application configuration
- database connection managers
- logging helpers
- caches
- service registries
The important Python-specific detail is this:
- In many languages, singleton patterns are built into class design.
- In Python, modules are already singletons in practice because a module is imported once and then reused.
That is why many Python developers ask a different question:
Do you really need a singleton class, or do you just need one shared object?
Why this matters
Choosing the wrong implementation can make code harder to understand.
For example:
- A decorator that turns a class into a function can break class behavior.
- A metaclass can work, but may be harder for beginners to read.
- A module-level instance is simple and unsurprising.
In Python, the most Pythonic solution is usually the one that is:
- easiest to read
- easiest to test
- least magical
- least likely to surprise other developers
So the real lesson is not just how to build a singleton, but when a simpler shared-object design is better.
Mental Model
Think of a singleton like a single reception desk in an office.
- Many people can ask for the desk.
- But the office should not create a new reception desk each time.
- Everyone is directed to the same one.
In Python, a module is like the building directory that already points everyone to the same desk.
A singleton class is more like adding a guard who checks:
- "Has the desk already been created?"
- If yes, return the existing one.
- If not, create it once.
The simpler the guard system, the easier it is for everyone else in the office to understand what is happening.
Syntax and Examples
1. Simplest Pythonic approach: module-level instance
# logger_service.py
class Logger:
def log(self, message):
print(f"LOG: {message}")
logger = Logger()
Use it like this:
from logger_service import logger
logger.log("Application started")
Why this is often preferred
- The module is imported once.
loggeris created once.- Other files reuse the same object.
- It is easy to understand.
2. Singleton with __new__
class Logger:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
print("__init__ called")
Usage:
a = Logger()
b = Logger()
print(a is b) # True
Important note
__new__ controls object creation, but __init__ may still run every time you call Logger() unless you guard against repeated initialization.
Example with safe one-time initialization:
class Logger:
_instance = None
_initialized = False
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if self._initialized:
return
self.logs = []
self._initialized = True
3. Singleton with a metaclass
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Logger(metaclass=SingletonMeta):
def __init__(self):
self.logs = []
Usage:
a = Logger()
b = Logger()
print(a is b) # True
This keeps Logger as a real class and is cleaner than turning the class into a function with a decorator.
4. Decorator approach that replaces the class
def singleton(cls):
instance = None
def wrapper(*args, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
return instance
return wrapper
This works for returning one instance, but it changes the decorated class name into a callable wrapper rather than preserving normal class behavior. That makes it less suitable when you want normal class semantics.
Step by Step Execution
Consider this example using __new__:
class Config:
_instance = None
_initialized = False
def __new__(cls):
if cls._instance is None:
print("Creating instance")
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if self._initialized:
print("Already initialized")
return
print("Running initialization")
self.settings = {"theme": "dark"}
self._initialized = True
x = Config()
y = Config()
print(x is y)
print(x.settings)
What happens step by step
x = Config()is executed.- Python calls
Config.__new__(Config). _instanceisNone, so a new object is created.- Python then calls
Config.__init__(x). _initializedisFalse, so initialization runs andsettingsis created.y = Config()is executed.- Python calls
Config.__new__(Config)again. _instancealready exists, so the same object is returned.- Python still calls
Config.__init__(y). - Because
_initializedis nowTrue, initialization is skipped. x is yprintsTrue.x.settingsprints{'theme': 'dark'}.
Expected output
Creating instance
Running initialization
Already initialized
True
{'theme': 'dark'}
Real World Use Cases
Shared configuration
A program may load configuration once and reuse it everywhere.
class AppConfig:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.debug = True
return cls._instance
Logging service
A single logger object can collect messages from different parts of the app.
Cache manager
A shared in-memory cache avoids creating separate caches in each module.
Database connection coordinator
Sometimes an app wants one connection manager object, even if the manager itself internally controls a pool.
Feature flag registry
A shared service can expose flags like is_beta_enabled across the application.
In practice, many of these are simpler as module-level objects rather than formal singleton classes.
Real Codebase Usage
In real projects, developers often use one of these patterns instead of a heavy singleton implementation:
Module-level shared object
# config.py
settings = {"debug": True}
This is the most common and readable option when one shared object is enough.
Lazy module-level factory
_instance = None
def get_logger():
global _instance
if _instance is None:
_instance = Logger()
return _instance
This gives lazy initialization without metaclasses.
Dependency injection
Instead of global access, the object is created once and passed where needed.
class UserService:
def __init__(self, logger):
self.logger = logger
This is common in testable codebases because it avoids hidden global state.
Guarded initialization
If using __new__, developers commonly add an initialization guard.
Validation and error handling
Singleton-like services often validate startup configuration once, then raise a clear error if setup is invalid.
Why metaclasses are less common
Metaclasses can be correct, but many teams avoid them unless they truly need class-level customization, because they increase mental overhead for readers.
Common Mistakes
Mistake 1: Forgetting that __init__ can run more than once
Broken example:
class Logger:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.logs = []
Problem:
- Every call to
Logger()resetsself.logs.
Fix:
class Logger:
_instance = None
_initialized = False
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if self._initialized:
return
self.logs = []
self._initialized = True
Mistake 2: Using == instead of is
a = Logger()
b = Logger()
print(a == b)
Problem:
==checks value equality.ischecks whether both names refer to the exact same object.
Use this:
print(a is b)
Mistake 3: Replacing the class with a function unintentionally
A decorator-based singleton may make this fail in surprising ways:
@singleton
class MyClass:
pass
print(type(MyClass))
Now MyClass may be a function-like wrapper, not the original class.
Mistake 4: Making code hard to test
Global singletons can leak state between tests.
Avoid this by:
- resetting shared state in tests
- using dependency injection when possible
- preferring simple factories for testable code
Mistake 5: Using a singleton where a module variable is enough
If all you need is one shared object, this is often simpler:
service = Service()
inside a module.
Comparisons
| Approach | Keeps real class? | Lazy creation? | Complexity | Common in Python? | Notes |
|---|---|---|---|---|---|
| Module-level instance | Yes | No, unless wrapped | Low | Very common | Simplest shared-object solution |
| Module-level factory | Yes | Yes | Low | Very common | Good balance of simplicity and laziness |
__new__ singleton | Yes | Yes | Medium | Sometimes | Must guard repeated __init__ |
| Metaclass singleton | Yes | Yes | High | Less common | Powerful, but more magical |
| Decorator returning wrapper function | No | Yes | Medium | Less common | Breaks normal class behavior |
| Decorator returning subclass | Mostly | Yes | High | Rare | Can complicate inheritance and super() |
__new__ vs metaclass
| Feature | __new__ | Metaclass |
|---|---|---|
| Beginner-friendly | More | Less |
| Affects one class directly | Yes | Yes |
| Reusable across many classes | Somewhat | Yes |
| Easy to read in teams | Usually | Sometimes not |
| Class semantics preserved | Yes | Yes |
Most Pythonic choice
For most code:
- Module-level object if you just need one shared instance.
- Module-level factory if you want lazy creation.
__new__or metaclass only if you truly need singleton behavior at the class level.
Cheat Sheet
Singleton quick reference in Python
Simplest shared object
# service.py
class Service:
pass
service = Service()
Lazy shared object
_instance = None
def get_service():
global _instance
if _instance is None:
_instance = Service()
return _instance
__new__ singleton
class Service:
_instance = None
_initialized = False
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if self._initialized:
return
self._initialized = True
Metaclass singleton
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
Key rules
- Use
is, not==, to verify singleton identity. __new__creates the object;__init__initializes it.__init__may run multiple times unless guarded.- Modules already act like shared singletons in normal imports.
- Prefer the least surprising design.
Edge cases
- Singletons can make tests harder.
- In multithreaded code, extra locking may be needed.
- Decorators that replace the class can break introspection and class methods.
FAQ
Why are modules often considered singletons in Python?
Because Python imports a module once and then reuses the same module object, so module-level state is naturally shared.
Is using a metaclass the best way to implement a singleton in Python?
Not always. It is powerful, but often more complex than necessary. A module-level object or factory is usually simpler.
What is the difference between __new__ and __init__ in a singleton?
__new__ controls object creation. __init__ configures the object after creation. In a singleton, __init__ may be called multiple times unless you guard it.
How do I check whether two singleton variables are the same object?
Use is:
a is b
Are singletons bad in Python?
Not automatically, but they can introduce hidden global state and make testing harder. Use them only when shared state is truly needed.
What is the most Pythonic singleton alternative?
Usually a module-level instance or a module-level getter function.
Can a decorator-based singleton break my class?
Yes. If the decorator returns a function wrapper, the original class identity and behavior may be lost.
Should I use a singleton for logging in Python?
Often no. Python's built-in logging module already manages shared logger behavior well.
Mini Project
Description
Build a small application-wide settings manager that behaves like a singleton. This project demonstrates how to ensure only one settings object exists while avoiding repeated initialization bugs.
Goal
Create a reusable Settings class where every call returns the same object and configuration is initialized only once.
Requirements
- Create a
Settingsclass using Python. - Ensure multiple
Settings()calls return the same object. - Prevent the configuration data from being reset on repeated construction.
- Add methods to set and get configuration values.
- Show that two variables reference the same instance.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.