Overview The Singleton pattern ensures a class has only one instance while providing a global access point to it. While often dismissed as an antipattern due to the dangers of shared global state, its true utility lies in **controlled instantiation**. This tutorial explores how to implement singletons in Python, why they can fail in multi-threaded environments, and how to use them effectively for lazy loading expensive resources. Prerequisites To follow this guide, you should understand Python classes, dunder methods like `__new__` and `__call__`, and basic concurrency concepts involving the `threading` module. Key Libraries & Tools - **threading**: A built-in Python module used to demonstrate and solve race conditions in instance creation. - **Metaclasses**: A deep-level Python feature used to create generic, reusable singleton logic. Code Walkthrough The Metaclass Approach Using a metaclass is the most robust way to implement a singleton. By overriding `__call__`, we intercept the class instantiation process. ```python class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: instance = super().__call__(*args, **kwargs) cls._instances[cls] = instance return cls._instances[cls] class DatabaseConfig(metaclass=SingletonMeta): def __init__(self): self.url = "sqlite:///dev.db" ``` Here, `SingletonMeta` maintains a dictionary of instances. When you call `DatabaseConfig()`, Python checks the dictionary first. If the instance exists, it returns it; otherwise, it creates one. Making it Thread-Safe In multi-threaded apps, two threads might check the dictionary simultaneously, creating two separate instances. We solve this with a double-checked locking pattern. ```python import threading class SafeSingletonMeta(type): _instances = {} _lock = threading.Lock() def __call__(cls, *args, **kwargs): with cls._lock: if cls not in cls._instances: instance = super().__call__(*args, **kwargs) cls._instances[cls] = instance return cls._instances[cls] ``` Syntax Notes - **__new__ vs __call__**: Overriding `__new__` handles instantiation at the class level, while `__call__` on a metaclass handles the "calling" of the class itself. - **Modules as Singletons**: In Python, a module is only initialized once per execution. For simple global state, a dedicated `.py` file is more idiomatic than a class-based singleton. Practical Examples Controlled instantiation is perfect for **Lazy Loading**. Imagine loading a massive Large Language Model. You don't want to load it at startup—only when the first prediction occurs. ```python class ModelLoader(metaclass=SingletonMeta): def __init__(self): print("Loading 10GB Model...") # Only runs once self.model = "Heavy Object" ``` Tips & Gotchas - **Testing Nightmare**: Singletons create hidden dependencies. If a test modifies a singleton's state, every subsequent test inherits that change. Always provide a way to reset state for unit tests. - **Private Constructors**: Unlike Java, Python cannot truly hide a constructor. Developers can always bypass your singleton logic by calling `__new__` directly.
Java
Products
TL;DR
ArjanCodes (3 mentions) discusses Java's role in object-oriented programming, as seen in videos like "We need to talk." and "5 Tips For Object-Oriented Programming Done Well - In Python."
- Sep 19, 2025
- Aug 22, 2025
- May 16, 2025
- Jul 1, 2022
- Jun 10, 2022