简单直接
发现很多朋友不知道单例如何实现,如果你想要求一个类一定是一个单例,那么最好的方式应该是用metaclass的方式,如下
1 2 3 4 5 6
| class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls]
|
用法如下:
1 2 3 4 5 6 7
| class MyClass(BaseClass): __metaclass__ = Singleton
class MyClass(BaseClass, metaclass=Singleton): pass
|
如果你不确定,但是在使用中需要缓存,那你最好写一个manager,缓存实例化的类,而不是在类这里定义一个单例
1 2 3 4 5 6 7 8 9
| class Manager(object): _cache = dict() _class_map = dict() @classmethod def get(cls, name, *arg, **kwargs) return cls._cache.setdefault(name, cls._class_map[name](*arg, **kwargs))
|
遇见多线程
感谢@MansfieldLee提到这个问题
一般来说,Python程序不太在乎多线程性能(因为GIL),人们通常利用协程+进程的方式解决问题。在协程中,以下代码块没有 await
,可以认为是同步的,因此单例的实现也是线程安全的
1 2 3 4 5
| def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls]
|
但是如果你需要在多线程中使用单例,那么你需要考虑线程安全的问题,这里给出一个线程安全的单例实现
1 2 3 4 5 6 7 8 9 10 11 12
| import threading
class Singleton(type): _instances = {} _lock = threading.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(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls]
|