Python Singleton Example

简单直接

发现很多朋友不知道单例如何实现,如果你想要求一个类一定是一个单例,那么最好的方式应该是用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
#Python2
class MyClass(BaseClass):
__metaclass__ = Singleton

#Python3
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
# 这个方法没有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]

但是如果你需要在多线程中使用单例,那么你需要考虑线程安全的问题,这里给出一个线程安全的单例实现

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-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]

🚀Python单例类Singleton样例
https://wh1isper.github.io/2022/11/13/2022-11-24-Python-Singleton-how-to/
作者
Wh1isper
发布于
2022年11月14日
许可协议