单例模式算是在python
用的比较多的设计模式,比较典型的应用场景包括:应用配置、日志文件、数据库调用…
其特点是在整个系统周期仅会初始化一次,因此可以减少内存占用空间以及避免多次实例化。比如类AppConfig
会在整个系统多次进行调用,如果每次调用都进行实例化,无疑会拖慢系统运行效率。
关于单例模式实现, 网上有很多花里胡哨的方法,这里介绍一种比较常见的方法。使用__new__
方法实现。
基本原理
python
中实例化一个类时会在调用__init__
方法前调用__new__
方法,其接受的第一个参数是类本身,其余参数和__init__
方法保持一致。因此可以在类中定义一个_instantce
变量,在第一个进行示例化后将实例化的类保存到_instance
中,之后实例化时只需判定_instance
变量是否为空即可。
以实现一个全局计数器类为例
一个最简单的版本,这种实现方法会多次调用__init__
函数,并且存在多线程重复实例化问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
class GlobalCount(object): _instance = None
def __new__(cls, *args, **kwargs): if cls._instance: print("跳过实例化") return cls._instance else: cls._instance = super(GlobalCount, cls).__new__(cls) return cls._instance
def __init__(self): self._count = 0
@property def count(self): return self._count
@count.setter def count(self, value): self._count = value
if __name__ == "__main__": for i in range(10): gc = GlobalCount() gc.count = gc.count + 1 print(gc.count)
|
运行这段代码结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 1 跳过实例化 1 跳过实例化 1 跳过实例化 1 跳过实例化 1 跳过实例化 1 跳过实例化 1 跳过实例化 1 跳过实例化 1 跳过实例化 1
|
可以看到,虽然避免了重复实例化的问题,但并没有达到我们想要的效果。
避免多次调用__init__函数
只需要在添加一个类变量:_init_flag
作为标志位即可。在__init__
方法内部将初始化相关的代码移动到if语句内,并在完成后将_init_flag
设为False
即可。
1 2 3 4 5 6 7 8 9 10 11 12
| class GlobalCount(object): _instance = None _init_flag = True def __new__(cls, *args, **kwargs): if cls._instance: print("跳过实例化") return cls._instance else: cls._instance = super(GlobalCount, cls).__new__(cls) return cls._instance ...
|
保证多线程安全
继续完善GlobalCount
类,目前在单线程下已经可以完美的工作了,但在多线程下有可能会存在多个线程同时进行实例化。可以使用threading
包中的lock
方法进行加锁处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import threading
class GlobalCount(object): _instance = None _init_flag = True _instance_lock = threading.Lock() def __new__(cls, *args, **kwargs): with GlobalCount._instance_lock: if cls._instance: print("跳过实例化") return cls._instance else: cls._instance = super(GlobalCount, cls).__new__(cls) return cls._instance
|
总结
总结一下,为了实现GlobalCount
类,我们用到了__new__方法、property语法糖以及threading
。