[Python设计模式] 第21章 计划生育——单例模式
github地址:https://github.com/cheesezh/python_design_patterns
單例模式
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例模式就能派上用場。
比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。
在 Python 中,我們可以用多種方法來實現單例模式。
使用模塊
其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導入時,會生成 .pyc 文件,當第二次導入時,就會直接加載 .pyc 文件,而不會再次執行模塊代碼。因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:
# mysingleton.py
class Singleton(object):def foo(self):pass
singleton = Singleton() 將上面的代碼保存在文件 mysingleton.py 中,要使用時,直接在其他文件中導入此文件中的對象,這個對象即是單例模式的對象
from a import singleton 使用裝飾器
def Singleton(cls):_instance = {}def _singleton(*args, **kargs):if cls not in _instance:_instance[cls] = cls(*args, **kargs)return _instance[cls]return _singleton@Singleton
class A(object):a = 1def __init__(self, x=0):self.x = xa1 = A(2)
a2 = A(3)
print(a1)
print(a2) <__main__.A object at 0x103496668>
<__main__.A object at 0x103496668> 使用類
class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instance 一般情況,大家以為這樣就完成了單例模式,但是這樣當使用多線程時會存在問題。
class Singleton(object):def __init__(self):pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instanceimport threadingdef task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start() <__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8>
<__main__.Singleton object at 0x1033e84a8> 看起來也沒有問題,那是因為執行速度過快,如果在init方法中有一些IO操作,就會發現問題了,下面我們通過time.sleep模擬
class Singleton(object):def __init__(self):import timetime.sleep(0.5)pass@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instanceimport threadingdef task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start() <__main__.Singleton object at 0x1033b79e8>
<__main__.Singleton object at 0x1033b7e10>
<__main__.Singleton object at 0x103401ef0>
<__main__.Singleton object at 0x103401c18>
<__main__.Singleton object at 0x103401048>
<__main__.Singleton object at 0x103401dd8>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401eb8>
<__main__.Singleton object at 0x103401a58>
<__main__.Singleton object at 0x103401fd0> 問題出現了!按照以上方式創建的單例,無法支持多線程。
解決辦法:加鎖!未加鎖部分并發執行,加鎖部分串行執行,速度降低,但是保證了數據安全
import time
import threadingclass Singleton(object):_instance_lock = threading.Lock()def __init__(self):time.sleep(0.5)@classmethoddef instance(cls, *args, **kwargs):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()
time.sleep(5)
obj = Singleton.instance()
print(obj) <__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00>
<__main__.Singleton object at 0x103401b00> 這樣就差不多了,但是還是有一點小問題,就是當程序執行時,執行了time.sleep(5)后,下面實例化對象時,此時已經是單例模式了,但我們還是加了鎖,這樣不太好,再進行一些優化,需要修改一下intance方法。
import time
import threadingclass Singleton(object):_instance_lock = threading.Lock()def __init__(self):time.sleep(0.5)@classmethoddef instance(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = Singleton(*args, **kwargs)return Singleton._instancedef task(arg):obj = Singleton.instance()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start()time.sleep(5)
obj = Singleton.instance()
print(obj) <__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550>
<__main__.Singleton object at 0x1033db550> 這種方式實現的單例模式,使用時會有限制,以后實例化必須通過obj = Singleton.instance(),如果用obj=Singleton(),這種方式得到的不是單例。
使用__new__方法(推薦使用,方便)
通過上面例子,我們可以知道,當我們實現單例時,為了保證線程安全需要在內部加入鎖。
我們知道,當我們實例化一個對象時,是先執行了類的__new__方法(我們沒寫時,默認調用object.__new__)實例化對象;然后再執行類的__init__方法,對這個對象進行初始化,所有我們可以基于這個機制,實現單例模式。
import threading
import time
class Singleton(object):_instance_lock = threading.Lock()def __init__(self):time.sleep(0.5)def __new__(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):Singleton._instance = object.__new__(cls) return Singleton._instanceobj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)def task(arg):obj = Singleton()print(obj)for i in range(10):t = threading.Thread(target=task,args=[i,])t.start() <__main__.Singleton object at 0x103483b70> <__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70>
<__main__.Singleton object at 0x103483b70> 采用這種方式的單例模式,以后實例化對象時,和平時實例化對象的方法一樣obj = Singleton()
使用metaclass
- 類由type創建,創建類時,type的__init__方法自動執行,類()執行type的__call__方法(類的__new__方法,類的__init__方法)
- 對象由類創建,創建對象時,類的__init__方法自動執行,對象()執行類的__call__方法
class Foo:def __init__(self):print("Foo __init__")def __call__(self, *args, **kwargs):print("Foo __call__")# 執行type的 __call__ 方法
# 調用 Foo類(是type的對象)的 __new__方法,用于創建對象
# 然后調用 Foo類(是type的對象)的 __init__方法,用于對對象初始化。
obj = Foo()# 執行Foo的 __call__ 方法
obj() Foo __init__
Foo __call__ class SingletonType(type):def __init__(self,*args,**kwargs):super(SingletonType,self).__init__(*args,**kwargs)def __call__(cls, *args, **kwargs): # 這里的cls,即Foo類print('cls',cls)obj = cls.__new__(cls,*args, **kwargs)cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)return objclass Foo(metaclass=SingletonType): # 指定創建Foo的type為SingletonTypedef __init__(self, name):self.name = namedef __new__(cls, *args, **kwargs):return object.__new__(cls)obj = Foo('xx') cls <class '__main__.Foo'> import threadingclass SingletonType(type):_instance_lock = threading.Lock()def __call__(cls, *args, **kwargs):if not hasattr(cls, "_instance"):with SingletonType._instance_lock:if not hasattr(cls, "_instance"):cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)return cls._instanceclass Foo(metaclass=SingletonType):def __init__(self,name):self.name = nameobj1 = Foo('name')
obj2 = Foo('name')
print(obj1)
print(obj2) <__main__.Foo object at 0x10348d908>
<__main__.Foo object at 0x10348d908> 參考文章: Python中的單例模式的幾種實現方式的及優化
轉載于:https://www.cnblogs.com/CheeseZH/p/9462254.html
總結
以上是生活随笔為你收集整理的[Python设计模式] 第21章 计划生育——单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: qq网名大全两个字女生
- 下一篇: Vue插槽 slot