本文首发于我的个人博客:Python单例类Singleton样例
简单直接
发现很多朋友不知道单例如何实现,如果你想要求一个类一定是一个单例,那么最好的方式应该是用metaclass的方式,如下
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]
用法如下:
#Python2
class MyClass(BaseClass):
__metaclass__ = Singleton
#Python3
class MyClass(BaseClass, metaclass=Singleton):
pass
如果你不确定,但是在使用中需要缓存,那你最好写一个manager,缓存实例化的类,而不是在类这里定义一个单例
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,可以认为是同步的,因此单例的实现也是线程安全的
# 这个方法没有await,是线程安全的
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
但是如果你需要在多线程中使用单例,那么你需要考虑线程安全的问题,这里给出一个线程安全的单例实现
import threading
class Singleton(type):
_instances = {}
_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
# 这里采用if-lock-if的方式,在大部分情况下,不需要加锁,因此性能更好
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]