前言
單例模式腹缩,顧名思義就是某個(gè)類只有一個(gè)實(shí)例胡控。它適用于那些只需要一個(gè)實(shí)例就可以實(shí)現(xiàn)所需功能的情況呜师,避免創(chuàng)建多個(gè)實(shí)例占用內(nèi)存空間框杜。
實(shí)現(xiàn)方法
- import 方法
Python 的模塊在導(dǎo)入時(shí)蝶押,會(huì)先查看sys.modules
是否存在該模塊對(duì)象踱蠢;如果存在,便直接使用這個(gè)模塊對(duì)象;否則加載該模塊茎截,并將其存入sys.modules
中苇侵。從中我們知道 Python 的模塊只會(huì)被導(dǎo)入一次,不會(huì)重復(fù)導(dǎo)入企锌。因此榆浓, Python 的模塊可以說(shuō)是天然的單例。
# foo.py
class Foo(object):
pass
f = Foo()
# use.py
from foo import f
- 使用 new 方法
當(dāng)我們實(shí)例化一個(gè)對(duì)象時(shí)撕攒,Python 會(huì)先調(diào)用__new__
方法創(chuàng)建一個(gè)實(shí)例陡鹃,再調(diào)用__init__
方法初始化這個(gè)實(shí)例。因此我們只要在實(shí)例的創(chuàng)建過(guò)程中進(jìn)行攔截抖坪,便可以實(shí)現(xiàn)單例萍鲸。
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
不過(guò),上面這種寫(xiě)法在多線程的情況下并不能保持單例擦俐,因此我們需要加鎖
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()
- 使用裝飾器
假如我們有許多的類需要實(shí)現(xiàn)單例模式脊阴,如果都給它們寫(xiě)一個(gè)__new__
方法,便會(huì)重復(fù)許多代碼捌肴。這時(shí)蹬叭,我們便可以使用裝飾器。
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()
上面這種寫(xiě)法會(huì)把 Foo 這個(gè)變量名變成 instance 函數(shù)状知,這樣便無(wú)法使用類方法
print(Foo) 結(jié)果為 <function singleton.<locals>.instance at 0x0000026F372B4158>
因此秽五,我們需要換一種寫(xiě)法
代碼參考: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__ 只會(huì)被調(diào)用一次
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
后記
綜上所述,我覺(jué)得最好的實(shí)現(xiàn)單例的方法就是 import 方法饥悴,簡(jiǎn)單直接坦喘。