前言
单例模式,顾名思义就是某个类只有一个实例。它适用于那些只需要一个实例就可以实现所需功能的情况,避免创建多个实例占用内存空间。
实现方法
- import 方法
Python 的模块在导入时,会先查看sys.modules
是否存在该模块对象;如果存在,便直接使用这个模块对象;否则加载该模块,并将其存入sys.modules
中。从中我们知道 Python 的模块只会被导入一次,不会重复导入。因此, Python 的模块可以说是天然的单例。
# foo.py
class Foo(object):
pass
f = Foo()
# use.py
from foo import f
- 使用 new 方法
当我们实例化一个对象时,Python 会先调用__new__
方法创建一个实例,再调用__init__
方法初始化这个实例。因此我们只要在实例的创建过程中进行拦截,便可以实现单例。
class Foo(object):
_singleton = None
def __new__(cls, *args, **kwargs):
if cls._singleton is None:
cls._singleton = object.__new__(cls)
return cls._singleton
if __name__ == '__main__':
f1 = Foo()
f2 = Foo()
assert f1 is f2
不过,上面这种写法在多线程的情况下并不能保持单例,因此我们需要加锁
import threading
import time
class Foo(object):
_singleton = None
_singleton_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if cls._singleton is None:
# time.sleep(0.3)
# cls._singleton = object.__new__(cls)
with cls._singleton_lock:
if cls._singleton is None:
cls._singleton = object.__new__(cls)
return cls._singleton
def print_instance():
print(Foo())
def main():
threads = [threading.Thread(target=print_instance) for i in range(5)]
for t in threads:
t.start()
if __name__ == '__main__':
main()
- 使用装饰器
假如我们有许多的类需要实现单例模式,如果都给它们写一个__new__
方法,便会重复许多代码。这时,我们便可以使用装饰器。
import threading
import time
def singleton(cls):
_singleton = {}
_singleton_lock = threading.Lock()
def instance(*args, **kwargs):
if cls.__name__ not in _singleton:
time.sleep(0.3)
with _singleton_lock:
if cls.__name__ not in _singleton:
_singleton[cls.__name__] = cls(*args, **kwargs)
return _singleton[cls.__name__]
return instance
@singleton
class Foo(object):
pass
def print_instance():
print(Foo())
def main():
threads = [threading.Thread(target=print_instance) for i in range(5)]
for t in threads:
t.start()
if __name__ == '__main__':
main()
上面这种写法会把 Foo 这个变量名变成 instance 函数,这样便无法使用类方法
print(Foo) 结果为 <function singleton.<locals>.instance at 0x0000026F372B4158>
因此,我们需要换一种写法
代码参考:Singleton
import functools
def singleton(cls):
''' Use class as singleton. '''
cls.__new_original__ = cls.__new__
@functools.wraps(cls.__new__)
def singleton_new(cls, *args, **kw):
it = cls.__dict__.get('__it__')
if it is not None:
return it
cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
it.__init_original__(*args, **kw)
return it
cls.__new__ = singleton_new
cls.__init_original__ = cls.__init__
# 保证原 cls.__init__ 只会被调用一次
cls.__init__ = object.__init__
return cls
#
# Sample use:
#
@singleton
class Foo:
def __new__(cls):
cls.x = 10
return object.__new__(cls)
def __init__(self):
assert self.x == 10
self.x = 15
assert Foo().x == 15
Foo().x = 20
assert Foo().x == 20
后记
综上所述,我觉得最好的实现单例的方法就是 import 方法,简单直接。