Phiên bản 1: classic gang of four
Key idea 1: Kiểm soát instantiation qua get_instance()
class ClassicSingleton:
_instance = None
def __init__(self):
raise RuntimeError("Call get_instance() instead")
@classmethod
def get_instance(cls):
if cls._instance is None:
instance = cls.__new__(cls)
cls._instance = instance
print("Singleton created (get_instance called)")
return cls._instance
Test:
s1 = ClassicSingleton.get_instance()
s2 = ClassicSingleton.get_instance()
print(s1 is s2) # True
print(id(s1) == id(s2)) # True — cùng memory address
Phiên bản 2: simple python singleton
Key idea 2: Kiểm soát qua __new__
class SimpleSingleton:
_instance = None
def __new__(cls):
if cls._instance is None:
print("Creating new instance via __new__")
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
print("__init__ called (always executes)")
Lưu ý: __new__ tạo instance, __init__ khởi tạo nó. Với Singleton, __init__ có thể bị gọi nhiều lần (mỗi lần SimpleSingleton() được gọi), nhưng __new__ chỉ tạo instance một lần.
Phiên bản 3: metaclass singleton
Key idea 3: Logic Singleton tách biệt trong Metaclass
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
print("SingletonMeta.__call__ invoked")
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
def business_logic(self):
print("Business logic here")
Test:
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
Key idea 4: Quan sát hành vi khi không tạo object
Nếu chỉ define class mà không có code tạo instance:
class Singleton(metaclass=SingletonMeta):
pass
# Không có gì in ra — singletonmeta.__call__ chưa được gọi
Đây xác nhận đây là lazy instantiation.
Thread-safe singleton trong thực tế
Key idea 5: Test với 10 threads
import threading
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def get_singleton():
s = ThreadSafeSingleton()
print(id(s))
threads = [threading.Thread(target=get_singleton) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
Kết quả: 10 dòng in ra cùng một memory address — chứng minh Singleton hoạt động đúng dù có 10 thread chạy đồng thời.