Explain Codes LogoExplain Codes Logo

Creating a Singleton in Python

python
singleton
metaclasses
decorators
Nikita BarsukovbyNikita Barsukov·Oct 17, 2024
TLDR

To create a singleton in Python, use the __new__ method to manage instance creation. Here's your go-to example:

class Singleton: _instance = None def __new__(cls): # Oh, you think you can just create a new instance? Think again! if not cls._instance: cls._instance = super().__new__(cls) return cls._instance # You get nothing. You lose. Good day, sir!

This enforces the class to have one instance only and provides a universal access point to that instance. Every Singleton() call returns the very same instance.

Python-flavored Singleton using decorators

Python's functools.lru_cache decorator offers a flavorful way to create a singleton with a single argument. It's thread-safe, efficient, and makes your code look cool!

import functools @functools.lru_cache(maxsize=None) def get_singleton(): class Singleton: pass return Singleton() # Seriously, they're like twins... or, wait. They're the same! first_instance = get_singleton() second_instance = get_singleton() assert first_instance is second_instance

Remember, decorator is the spice of life, but too much can spoil the broth.

Watch your step! Common singleton pitfalls

While flexing your muscles with singletons, watch out for these potential banana peels: tight coupling, hidden dependencies, reduced flexibility, and compromises to the testability of your code.

Python modules: Singletons in disguise

Did you know? Python modules are the underdogs of the singleton world. A module is only imported once, so its variables are initialized one time only. Module-level variables can serve as singleton attributes, and functions as singleton methods.

# my_singleton.py _some_data = "Initial data" def get_data(): return _some_data def set_data(new_data): global _some_data _some_data = new_data # All your data are belong to us!

Visualization

Visualise singletons using a private room key concept:

class PrivateRoom: _instance = None key = '🔑' def __new__(cls): # It's like Hotel California. You can check out any time you want, but you can never leave! if cls._instance is None: cls._instance = super().__new__(cls) cls._instance.key = '🔑 (Used)' return cls._instance

Accessing the Singleton Private Room:

First Visit: Room(key='🔑') # Behind door number 1, we have a brand new room! Second Visit: Room(key='🔑 (Used)') # Sorry, no new rooms. Only the one you've been to.

Key takeaway: One room, same key – same private room, everytime you knock.

Real-world Singletons

Loggers

Singletons keep your log files tidy by using one logger object. Less mess, less stress!

Config objects

Consistent app-wide settings? Singleton config objects have you covered.

Connection pools

Managing database connections can be like herding cats. Singleton based connection pools can tame this feisty data beast!

Metaclasses for the win

Looking for a singleton solution that puts aside the Python 2 vs 3 feud? Join team metaclass:

class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): # Metaclasses: Because who needs simple when you can have fancy? if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class Singleton(metaclass=SingletonMeta): pass