0
0
PythonHow-ToIntermediate · 3 min read

How to Use Metaclass for Singleton in Python: Simple Guide

Use a metaclass that overrides the __call__ method to control instance creation and store a single instance. This way, every time the class is called, it returns the same object, implementing the singleton pattern.
📐

Syntax

A metaclass for singleton overrides the __call__ method to check if an instance already exists. If it does, it returns that instance; otherwise, it creates and stores a new one.

The class using this metaclass will then only ever have one instance.

python
class SingletonMeta(type):
    _instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class MyClass(metaclass=SingletonMeta):
    pass
💻

Example

This example shows how the metaclass ensures only one instance of MyClass is created. Even if you try to create multiple objects, they all point to the same instance.

python
class SingletonMeta(type):
    _instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class MyClass(metaclass=SingletonMeta):
    def __init__(self, value):
        self.value = value

obj1 = MyClass(10)
obj2 = MyClass(20)

print(obj1.value)  # Output: 10
print(obj2.value)  # Output: 10
print(obj1 is obj2)  # Output: True
Output
10 10 True
⚠️

Common Pitfalls

One common mistake is storing the instance as a class variable on the metaclass itself, which can cause issues if multiple singleton classes use the same metaclass. Instead, store the instance per class to support multiple singletons.

Also, forgetting to override __call__ properly will break the singleton behavior.

python
class WrongSingletonMeta(type):
    _instance = None  # Shared across all classes using this metaclass

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

# Correct way stores instance per class:
class CorrectSingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]
📊

Quick Reference

  • Metaclass: Controls class creation and instance creation.
  • Override __call__: To control how instances are made.
  • Store instance per class: Use a dictionary to support multiple singleton classes.
  • Use in class: Set metaclass=YourSingletonMeta in class definition.

Key Takeaways

Override the metaclass __call__ method to control instance creation for singleton behavior.
Store singleton instances per class to support multiple singleton classes safely.
Use metaclass by setting metaclass=SingletonMeta in your class definition.
Singleton metaclasses ensure only one instance of a class exists during runtime.
Avoid sharing instance variables across classes in the metaclass to prevent bugs.