SoFunction
Updated on 2025-04-14

Summary of various methods for implementing singleton pattern in Python

1. What is singleton pattern?

Singleton Pattern is a commonly used software design pattern that ensures that a class has only one instance and provides a global access point to access this instance. This pattern is useful in scenarios where the number of instances is required, the system resources are saved, or the global consistency is ensured.

1.1 Features of singleton mode

  • Uniqueness: Make sure that only one instance exists in a class
  • Global access: Provides global access points, usually implemented through class methods
  • Delay initialization: In most implementations, instances are created when the first time they are requested

1.2 Application scenarios of singleton mode

  • Configuration management (such as database configuration, application settings)
  • Logger
  • Thread pool, connection pool and other resource management
  • Cache system
  • Device drivers (such as printers)

2. Various methods to implement singleton pattern in Python

As a flexible language, Python provides multiple ways to implement singleton patterns. Below we will introduce the implementation principles, advantages and disadvantages and applicable scenarios of each method in detail.

2.1 Using modules to implement single cases

Python's modules are a natural singleton pattern, because modules will be initialized during the first import, and subsequent imports will directly use the loaded modules.

# singleton_module.py
class SingletonClass:
    def __init__(self):
         = None
    
    def do_something(self):
        print(f"Doing something with value: {}")

singleton_instance = SingletonClass()

# Use in other filesfrom singleton_module import singleton_instance

singleton_instance.value = 42
singleton_instance.do_something()

advantage

  • Simple and intuitive, native Python support
  • Thread-safe (module import is thread-safe in Python)

shortcoming

  • Unable to delay initialization, create an instance when the module is loaded
  • Not clear enough, may be misused

2.2 Using decorator to implement a single case

Decorators are a very powerful feature in Python that can be used to implement singleton mode.

def singleton(cls):
    instances = {}
    
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

@singleton
class SingletonClass:
    def __init__(self, value):
         = value
    
    def do_something(self):
        print(f"Doing something with value: {}")

# useinstance1 = SingletonClass(42)
instance2 = SingletonClass(99)

print(instance1 is instance2)  # Output: Trueprint()         # Output: 42print()         # Output: 42

advantage

  • Concise code, reusable
  • Can be applied to any class
  • Delay initialization

shortcoming

  • The instance is stored in the closure of the decorator and may not be easy to understand
  • Need to deal with thread safety issues (the thread safety version will be introduced later)

2.3 Implementing singletons using class methods (classic implementation)

This is the most traditional singleton implementation method, through coverage__new__Method to control the creation of an instance.

class SingletonClass:
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, value):
         = value
    
    def do_something(self):
        print(f"Doing something with value: {}")

# useinstance1 = SingletonClass(42)
instance2 = SingletonClass(99)

print(instance1 is instance2)  # Output: Trueprint()         # Output: 99 (note here!)print()         # Output: 99

Notice: There is a potential problem here, the property value will be reset every time the initialization is initialized. To solve this problem, the implementation can be modified:

class SingletonClass:
    _instance = None
    _initialized = False
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, value):
        if not self.__class__._initialized:
             = value
            self.__class__._initialized = True

advantage

  • Clear and intuitive, in line with traditional object-oriented programming habits
  • Delay initialization

shortcoming

  • __init__May be called multiple times and require additional processing
  • Need to deal with thread safety issues

2.4 Implementing single case using metaclasses

Metaclasses are advanced features in Python. They can control the creation process of classes and are very suitable for implementing singleton patterns.

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 SingletonClass(metaclass=SingletonMeta):
    def __init__(self, value):
         = value
    
    def do_something(self):
        print(f"Doing something with value: {}")

# useinstance1 = SingletonClass(42)
instance2 = SingletonClass(99)

print(instance1 is instance2)  # Output: Trueprint()         # Output: 42print()         # Output: 42

advantage

  • It is more in line with the concept of singletons rather than instances
  • Can be inherited, subclasses are also single cases
  • Elegant code, hiding implementation details

shortcoming

  • Metaclass concept is more complex and not friendly to beginners
  • Need to understand Python's metaclass mechanism

2.5 Using thread-safe singleton implementation

In a multithreaded environment, the simple singleton implementation described above may create multiple instances. Here is a thread-safe version:

import threading

class SingletonClass:
    _instance = None
    _lock = ()
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                # Check again, because other threads may have created instances while waiting for lock                if not cls._instance:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, value):
        with self.__class__._lock:
            if not hasattr(self, 'value'):
                 = value

# Or use the thread-safe version of the decoratorfrom functools import wraps
import threading

def synchronized(lock):
    def wrapper(f):
        @wraps(f)
        def inner_wrapper(*args, **kwds):
            with lock:
                return f(*args, **kwds)
        return inner_wrapper
    return wrapper

def singleton(cls):
    instances = {}
    lock = ()
    
    @synchronized(lock)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

advantage

  • Thread-safe, suitable for multi-threaded environments
  • Double check lock mode reduces lock overhead

shortcoming

  • Increased code complexity
  • Locking mechanism brings certain performance overhead

3. Advanced topics of singleton mode

3.1 Singleton and Inheritance

Special attention should be paid when combining singleton pattern with inheritance. When using metaclass implementations, subclasses will automatically become singletons:

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 BaseClass(metaclass=SingletonMeta):
    pass

class ChildClass(BaseClass):
    pass

a = BaseClass()
b = BaseClass()
c = ChildClass()
d = ChildClass()

print(a is b)  # True
print(c is d)  # True
print(a is c)  # False

3.2 Singleton and deserialization

When a singleton object is serialized and deserialized, it may destroy the singleton property. To maintain a singleton, it can be achieved__reduce__method:

import pickle

class SingletonClass:
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self, value):
        if not hasattr(self, 'value'):
             = value
    
    def __reduce__(self):
        return (self.__class__, (,))

# testinstance1 = SingletonClass(42)
serialized = (instance1)
instance2 = (serialized)

print(instance1 is instance2)  # Output: True

3.3 Singleton and unit tests

Singleton pattern can present challenges for unit testing, because the state of singletons is shared between tests. Solutions include:

  • Reset singleton status before testing
  • Use dependency injection instead of direct singleton access
  • Create a replaceable singleton implementation for tests
class DatabaseConnection:
    _instance = None
    
    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance
    
    @classmethod
    def _clear_instance(cls):
        """Test special method, reset singleton"""
        cls._instance = None

# In testingdef test_database():
    conn1 = DatabaseConnection.get_instance()
    # test...    DatabaseConnection._clear_instance()  # Reset status    conn2 = DatabaseConnection.get_instance()
    assert conn1 is not conn2  # New instance

4. Alternatives to singleton pattern

While singleton pattern is useful, it also has some disadvantages (such as global state, difficult to test, etc.), and the following alternatives can be considered in some cases:

4.1 Dependency injection

class AppConfig:
    def __init__(self, config_file):
         = self._load_config(config_file)
    
    def _load_config(self, config_file):
        # Load configuration        pass

# Create and inject during application initializationconfig = AppConfig("")
app = Application(config)

4.2 Module-level variables

For simple scenarios, it may be easier to use module-level variables directly than a full singleton pattern:

# 
config_data = {}

def init_config(config_file):
    global config_data
    # Load configuration to config_data
# useimport config
config.init_config("")
print(config.config_data)

4.3 Borg mode (shared status mode)

Borg mode allows multiple instances to be created, but share state:

class Borg:
    _shared_state = {}
    
    def __init__(self):
        self.__dict__ = self._shared_state

class YourClass(Borg):
    def __init__(self, arg):
        super().__init__()
        if 'arg' not in self.__dict__:
             = arg

# usea = YourClass(42)
b = YourClass(99)
print()  # 42
print()  # 42
print(a is b)  # False

5. Best Practices and Precautions

  1. Use single cases with caution: Singleton is essentially a global state, overuse can make the code difficult to test and maintain
  2. Consider thread safety: Especially in web applications or multi-threaded environments
  3. Documentation: Explain that the class is a singleton and how to use it correctly
  4. Avoid complex initialization: The initialization of singletons should be simple to avoid circular dependencies
  5. Consider alternatives: Evaluate whether a singleton is really needed or is there a better design model

6. Summary

Python provides a variety of ways to implement singleton patterns, each with its applicable scenarios:

  • Simple scene: Use module-level variables or decorators
  • Traditional object-oriented:use__new__method
  • Advanced requirements: Use metaclasses
  • Multi-threaded environment: Ensure thread-safe implementation

Which implementation you choose depends on specific requirements, team familiarity, and project size. Remember that design patterns are tools rather than goals, and the most appropriate solution should be chosen based on the actual problem.

7. Complete sample code

Here is a complete, thread-safe, serialized singleton implementation:

import threading
import pickle

class Singleton(type):
    _instances = {}
    _lock = ()
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            with cls._lock:
                if cls not in cls._instances:
                    cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]
    
    def __reduce__(self):
        return (self.__class__, ())

class DatabaseConnection(metaclass=Singleton):
    def __init__(self, connection_string=None):
        if not hasattr(self, '_initialized') or not self._initialized:
            self.connection_string = connection_string
            self._initialized = True
            # Actual connection initialization code            print(f"Initializing database connection to {self.connection_string}")
    
    def execute_query(self, query):
        print(f"Executing query: {query}")
        # The code to actually execute the query        return "query results"

# testdef test_singleton():
    # Created for the first time    db1 = DatabaseConnection("mysql://localhost:3306/mydb")
    # Second attempt to create - the same instance should be returned    db2 = DatabaseConnection("postgres://localhost:5432/mydb")
    
    print(db1 is db2)  # True
    print(db1.connection_string)  # mysql://localhost:3306/mydb
    print(db2.connection_string)  # mysql://localhost:3306/mydb
    
    # Test serialization    serialized = (db1)
    db3 = (serialized)
    print(db1 is db3)  # True
    
    # Test thread safety    def create_instance():
        instance = DatabaseConnection("thread_test")
        print(instance.connection_string)
    
    threads = []
    for i in range(5):
        t = (target=create_instance)
        (t)
        ()
    
    for t in threads:
        ()

if __name__ == "__main__":
    test_singleton()

This implementation includes:

  • Thread-safe (using double check locking)
  • Serialization support (through__reduce__
  • Prevent multiple initializations (using_initializedLogo)
  • Clear initialization output

Hopefully this detailed guide can help you fully understand the implementation of singleton pattern in Python!

The above is a detailed summary of the various methods of Python implementing singleton mode. For more information about Python implementing singleton mode, please pay attention to my other related articles!