How to create a singleton class in Python
Learn to create a singleton class in Python. Explore different methods, tips, real-world applications, and how to debug common errors.

A singleton class ensures only one instance of that class ever exists. This design pattern is crucial to manage shared resources, like database connections or global configuration settings, across your application.
You'll explore several techniques to implement singletons in Python. You'll find practical tips, see real-world applications, and get debugging advice so you can confidently apply this powerful design pattern.
Basic implementation with class variable
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)--OUTPUT--TRUE
The key to this implementation is overriding the __new__ method, which controls object creation. You're essentially intercepting the instantiation process before it happens. The class variable _instance acts as a cache for the single object.
The first time you call Singleton(), _instance is None, so a new object is created and stored. Every time after that, the if condition fails, and the method returns the already-existing object stored in _instance. This ensures that no matter how many times you try to instantiate the class, you always get back the very first object created.
Common implementation techniques
Building on the basic __new__ implementation, you can also create singletons with decorators, add initialization checks, or leverage the underlying power of metaclasses.
Using a decorator for singleton creation
def singleton(cls):
instances = {}
def get_instance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return get_instance
@singleton
class MyClass:
pass
a = MyClass()
b = MyClass()
print(a is b)--OUTPUT--TRUE
This approach uses a decorator to wrap your class, making the singleton logic reusable and clean. The @singleton decorator replaces your class with an inner function, get_instance, which handles the object creation process.
- A dictionary named
instancesacts as a cache for any created objects. - The first time you call
MyClass(), a new instance is created and stored in theinstancesdictionary. - Every subsequent call simply retrieves the existing instance from the dictionary, guaranteeing only one object is ever made.
Using __new__ method with initialization check
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self, value=None):
if not self._initialized:
self.value = value
self._initialized = True
s1 = Singleton("first")
s2 = Singleton("second")
print(s1.value, s2.value, s1 is s2)--OUTPUT--first first True
A tricky part of singletons is that __init__ gets called every time you try to create an instance, which can overwrite your object's initial state. This implementation solves that problem with a simple flag.
- An
_initializedflag is set on the instance only when it's first created inside__new__. - The
__init__method checks this flag before running. It only executes its logic once. - This prevents re-initialization, which is why
s2 = Singleton("second")doesn't change the value to "second". The original instance froms1is preserved.
Using metaclasses for singleton pattern
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 MySingleton(metaclass=SingletonMeta):
pass
instance1 = MySingleton()
instance2 = MySingleton()
print(instance1 is instance2)--OUTPUT--TRUE
This is a more advanced approach that uses a metaclass to control how the class itself behaves. The metaclass, SingletonMeta, intercepts the class instantiation process by defining a custom __call__ method. This method is what runs whenever you try to create an object, like with MySingleton().
- A dictionary named
_instancesholds the single object for each class using this metaclass. - The first time you call the class, it’s not in the dictionary, so a new instance is created and stored.
- Every call after that simply returns the already-existing instance from the dictionary.
Advanced singleton patterns
While the common patterns work for many cases, you'll often need more robust solutions for handling concurrency, on-demand creation, and dynamic configurations.
Thread-safe singleton implementation
import threading
class Singleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)--OUTPUT--TRUE
When multiple threads run at once, you risk a race condition where each thread creates its own instance, breaking the singleton pattern. This implementation solves that problem with a threading.Lock, which is crucial for concurrent applications.
- The
with cls._lock:statement acts as a gatekeeper, ensuring only one thread can execute the creation logic at a time. - The first thread to acquire the lock checks if
cls._instanceisNone, creates the object, and then releases the lock. - Any other threads must wait. By the time they get the lock, the instance already exists, so they just get the existing object.
Lazy initialization with get_instance() method
class LazySingleton:
__instance = None
@classmethod
def get_instance(cls):
if not cls.__instance:
cls.__instance = LazySingleton()
return cls.__instance
singleton1 = LazySingleton.get_instance()
singleton2 = LazySingleton.get_instance()
print(singleton1 is singleton2)--OUTPUT--TRUE
This pattern is 'lazy' because it delays creating the object until you actually need it. You don't call the constructor directly. Instead, you use the get_instance() class method to fetch the object.
- The first time you call
get_instance(), it sees that the private__instancevariable is empty and creates the object. - On every following call, it finds the existing object and simply returns that, ensuring you always get the same one.
Singleton with configuration parameters
class ConfigurableSingleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.configure(*args, **kwargs)
return cls._instance
def configure(self, config_value=None):
self.config = config_value
s = ConfigurableSingleton("initial config")
print(s.config)
s2 = ConfigurableSingleton("new config")
print(s.config, s2.config)--OUTPUT--initial config
initial config initial config
This pattern lets you set up your singleton with initial parameters, but only the first time it's created. A separate configure method handles the setup, and it's only called once from within the __new__ method.
- The configuration logic is placed inside the
if cls._instance is None:block, guaranteeing it runs only during the very first instantiation. - Because of this, any later attempts to create an instance, like with
ConfigurableSingleton("new config"), simply return the existing object. The original configuration is locked in and won't be overwritten.
Move faster with Replit
Replit is an AI-powered development platform with all Python dependencies pre-installed, so you can skip setup and start coding instantly. You can go from understanding a concept, like the singleton pattern, to applying it without wrestling with environment configurations.
Instead of piecing together individual techniques, you can use Agent 4 to build complete, working applications. It handles the entire development lifecycle—from writing code and connecting to databases to managing APIs and deploying your project—all from a simple description of what you want to build.
- A global configuration manager that ensures every part of your application uses the same API keys and settings.
- A centralized logging service that collects messages from different modules into a single, organized stream.
- A database connection handler that manages a shared pool of connections, improving your app's performance and resource use.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
While powerful, singletons introduce unique challenges with inheritance, serialization, and copying that can unexpectedly break the one-instance rule.
Inheritance can cause trouble if a subclass and its parent singleton class end up sharing the same instance. For example, if DatabaseConnection is a singleton and you create a subclass called PostgresConnection, you expect an instance of PostgresConnection, not the parent class. A basic singleton implementation might incorrectly return the parent instance for both.
- The solution is to make your singleton mechanism class-aware.
- Instead of a single
_instancevariable, use a dictionary that maps each class to its own unique instance. - This ensures that when you instantiate a subclass, it gets its own singleton instance, separate from the parent's. The metaclass and decorator patterns often handle this correctly.
Serializing a singleton with a tool like pickle and then deserializing it can accidentally create a new instance, defeating the purpose of the pattern. This happens because unpickling typically bypasses the class's custom __new__ or __call__ logic that enforces the singleton behavior.
- To fix this, you can tell
picklehow to find the existing instance instead of creating a new one. - You do this by implementing the
__reduce__method in your singleton class. - This method should return a callable—like a static
get_instance()method—thatpicklewill use during deserialization to retrieve the one true instance.
Similarly, using copy.deepcopy() on a singleton instance will create a duplicate, breaking the pattern. The function is designed to create a completely new and independent copy of an object, which is the exact opposite of what you want with a singleton.
- You can prevent this by overriding the
__deepcopy__method. - Inside your custom
__deepcopy__method, you simply ignore the request to create a new object and instead returnself. - This effectively intercepts the copy operation and ensures that any attempt to deep-copy the singleton just returns a reference to the existing instance.
Handling inheritance in singleton classes
Inheritance can trip up a simple singleton. When a child class extends a singleton parent, it can unexpectedly create a new, separate instance instead of returning the existing one. As the following code shows, this means parent is child will be false.
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
class ChildSingleton(Singleton):
pass
parent = Singleton()
child = ChildSingleton()
print(parent is child) # Will print False
The problem is that the cls._instance assignment creates a separate attribute for the child class. ChildSingleton doesn't see the parent's existing object and makes its own, resulting in two different instances. The following code shows how to fix this.
class Singleton:
_instances = {}
def __new__(cls):
if cls not in cls._instances:
cls._instances[cls] = super().__new__(cls)
return cls._instances[cls]
class ChildSingleton(Singleton):
pass
parent = Singleton()
child = ChildSingleton()
print(parent is child) # Still False, but each class has its own singleton
This solution uses a dictionary, _instances, to give each class its own singleton. Now, Singleton and ChildSingleton each manage a unique instance, which is why parent is child remains False. This ensures that each subclass behaves as a proper singleton without interfering with its parent or siblings. You'll want this pattern whenever you're creating a hierarchy of classes that all need to follow the singleton design pattern independently.
Fixing serialization issues with pickle
Serializing a singleton with Python's pickle module can unexpectedly break the one-instance rule. When you deserialize the object, pickle bypasses your singleton's creation logic and simply makes a new instance. The following code shows exactly how this happens, resulting in two separate objects.
import pickle
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
s1 = Singleton()
serialized = pickle.dumps(s1)
s2 = pickle.loads(serialized)
print(s1 is s2) # Will print False - singleton pattern broken
The pickle.loads() function reconstructs the object from its byte stream, bypassing the __new__ method entirely. This creates a fresh instance instead of returning the existing one. The following code shows how to fix this.
import pickle
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __reduce__(self):
return (self.__class__, ())
s1 = Singleton()
serialized = pickle.dumps(s1)
s2 = pickle.loads(serialized)
print(s1 is s2) # Now prints True
The fix is to implement the __reduce__ method, which tells pickle how to handle deserialization. By returning (self.__class__, ()), you're instructing pickle to call the class constructor again. This call is intercepted by your __new__ method, which correctly returns the existing instance instead of creating a new one. This simple override ensures that even after being "pickled" and "unpickled," you're still working with the one true singleton object.
Preventing singleton pattern breaking with copy.deepcopy()
Using copy.deepcopy() on a singleton is a classic pitfall because it's built to do the exact opposite of what you want—create a new, independent object. This action bypasses your singleton logic entirely. The following code shows this break in action.
import copy
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
original = Singleton()
copied = copy.deepcopy(original)
print(original is copied) # Will print False
The copy.deepcopy() function is meant to create a fresh copy, so it ignores the singleton's __new__ method entirely. This results in a second, unwanted instance. See how to override this behavior in the code below.
import copy
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __deepcopy__(self, memo):
return self
original = Singleton()
copied = copy.deepcopy(original)
print(original is copied) # Now prints True
To fix this, you override the __deepcopy__ method. Instead of letting it create a new object, your custom method simply returns self. This effectively tells Python to return the existing instance whenever a deep copy is requested. You'll need to watch out for this issue anytime your code or a library you're using might try to duplicate objects, as it can silently break your singleton's guarantee of a single instance.
Real-world applications
Now that you've navigated the technical challenges, you can apply singletons to manage shared resources like a central logger or database connection pool.
Using Logger singleton for application logging
This pattern ensures that no matter how many Logger objects you instantiate, they all point to the same instance, consolidating messages from across your application.
class Logger:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.log_history = []
return cls._instance
def log(self, message):
self.log_history.append(message)
print(f"LOG: {message}")
logger1 = Logger()
logger2 = Logger()
logger1.log("User login successful")
logger2.log("Database query executed")
print(logger1.log_history)
The __new__ method intercepts object creation, ensuring only one Logger instance ever exists. On the first call, it creates the object and attaches a log_history list. Any subsequent call to Logger() doesn't create a new object; it just returns the original one.
- This is why
logger1andlogger2are the same. When you call thelogmethod on either variable, you are modifying the same shared list. The final printout confirms both messages were captured in one place.
Implementing a DBConnectionManager for database access
A DBConnectionManager singleton guarantees your application uses one and only one database connection, preventing resource exhaustion and improving performance.
class DBConnectionManager:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Database connection established"
return cls._instance
def execute_query(self, query):
return f"Executing: {query} on {self.connection}"
db1 = DBConnectionManager()
db2 = DBConnectionManager()
print(db1.execute_query("SELECT * FROM users"))
print(db1 is db2)
The __new__ method intercepts object creation. On the first call to DBConnectionManager(), it creates the instance and sets the connection attribute. Any subsequent call bypasses this creation logic and just returns that original object.
- This is why
db1anddb2are identical—they both point to the same object in memory. - As a result, when you call
execute_queryon either variable, you're using the exact same instance and its singleconnectionattribute, which is confirmed by the output.
Get started with Replit
Turn your knowledge into a real tool. Describe what you want to build to Replit Agent, like “a centralized logger for my app” or “a settings manager that uses a single configuration.”
Replit Agent will write the code, test for errors, and deploy your application for you. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

.png)

.png)