Python實(shí)現(xiàn)單例模式的最佳方法匯總
Python中實(shí)現(xiàn)單例模式的最佳方法
技術(shù)背景
單例模式是一種創(chuàng)建型設(shè)計(jì)模式,它確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)來獲取這個(gè)實(shí)例。在Python中,有多種方式可以實(shí)現(xiàn)單例模式,不同的實(shí)現(xiàn)方式各有優(yōu)缺點(diǎn),適用于不同的場景。
實(shí)現(xiàn)步驟
方法1:使用裝飾器
def singleton(class_): instances = {} def getinstance(*args, **kwargs): if class_ not in instances: instances[class_] = class_(*args, **kwargs) return instances[class_] return getinstance @singleton class MyClass(BaseClass): pass
- 優(yōu)點(diǎn):裝飾器的使用方式直觀,比多重繼承更具可讀性。
- 缺點(diǎn):使用
MyClass()
創(chuàng)建的對象是單例對象,但MyClass
本身是一個(gè)函數(shù),不是類,因此不能調(diào)用類方法。
方法2:使用基類
class Singleton(object): _instance = None def __new__(class_, *args, **kwargs): if not isinstance(class_._instance, class_): class_._instance = object.__new__(class_, *args, **kwargs) return class_._instance class MyClass(Singleton, BaseClass): pass
- 優(yōu)點(diǎn):是一個(gè)真正的類。
- 缺點(diǎn):涉及多重繼承,
__new__
方法可能會(huì)在從第二個(gè)基類繼承時(shí)被覆蓋,需要更多的思考。
方法3:使用元類
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] # Python2 class MyClass(BaseClass): __metaclass__ = Singleton # Python3 class MyClass(BaseClass, metaclass=Singleton): pass
- 優(yōu)點(diǎn):是一個(gè)真正的類,自動(dòng)處理繼承問題,合理使用了元類的特性。
- 缺點(diǎn):相對復(fù)雜,可能會(huì)在序列化時(shí)出現(xiàn)問題。
方法4:裝飾器返回同名類
def singleton(class_): class class_w(class_): _instance = None def __new__(class_, *args, **kwargs): if class_w._instance is None: class_w._instance = super(class_w, class_).__new__(class_, *args, **kwargs) class_w._instance._sealed = False return class_w._instance def __init__(self, *args, **kwargs): if self._sealed: return super(class_w, self).__init__(*args, **kwargs) self._sealed = True class_w.__name__ = class_.__name__ return class_w @singleton class MyClass(BaseClass): pass
- 優(yōu)點(diǎn):是一個(gè)真正的類,自動(dòng)處理繼承問題。
- 缺點(diǎn):創(chuàng)建每個(gè)新類時(shí)可能會(huì)有開銷,
_sealed
屬性的作用不太明確,無法使用super()
調(diào)用基類的同名方法。
方法5:使用模塊
將需要作為單例的類和相關(guān)屬性、方法定義在一個(gè)模塊中,由于模塊只會(huì)被導(dǎo)入一次,因此模塊中的全局變量和函數(shù)可以作為單例使用。
# singleton.py class MyClass: def foo(self): pass my_singleton = MyClass()
# main.py from singleton import my_singleton my_singleton.foo()
- 優(yōu)點(diǎn):簡單直接。
- 缺點(diǎn):不是懶加載的,模塊導(dǎo)入時(shí)就會(huì)創(chuàng)建實(shí)例。
核心代碼
以下是使用元類實(shí)現(xiàn)單例模式的代碼示例:
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] class Logger(metaclass=Singleton): pass logger1 = Logger() logger2 = Logger() print(logger1 is logger2) # 輸出: True
最佳實(shí)踐
- 使用元類:元類是實(shí)現(xiàn)單例模式的推薦方法,它可以自動(dòng)處理繼承問題,并且是一個(gè)真正的類。
- 考慮使用模塊:如果單例模式的實(shí)現(xiàn)比較簡單,且不需要懶加載,可以考慮使用模塊來實(shí)現(xiàn)。
常見問題
- 序列化問題:使用元類實(shí)現(xiàn)的單例模式在序列化時(shí)可能會(huì)出現(xiàn)問題,因?yàn)榉葱蛄谢瘯r(shí)不會(huì)調(diào)用
__call__
方法??梢允褂没惱^承和__new__
方法來解決這個(gè)問題。 - 線程安全問題:在多線程環(huán)境下,需要確保單例模式的實(shí)現(xiàn)是線程安全的。可以使用鎖機(jī)制來保證線程安全,例如在元類的
__call__
方法中使用鎖。 __init__
方法多次調(diào)用問題:在某些實(shí)現(xiàn)中,__init__
方法可能會(huì)被多次調(diào)用??梢允褂靡粋€(gè)標(biāo)志位來確保__init__
方法只被調(diào)用一次。
以上就是Python實(shí)現(xiàn)單例模式的最佳方法匯總的詳細(xì)內(nèi)容,更多關(guān)于Python單例模式實(shí)現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GPU狀態(tài)監(jiān)測?nvidia-smi?命令的用法詳解
這篇文章主要介紹了GPU狀態(tài)監(jiān)測?nvidia-smi?命令的用法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11在python中實(shí)現(xiàn)同行輸入/接收多個(gè)數(shù)據(jù)的示例
今天小編就為大家分享一篇在python中實(shí)現(xiàn)同行輸入/接收多個(gè)數(shù)據(jù)的示例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07python神經(jīng)網(wǎng)絡(luò)facenet人臉檢測及keras實(shí)現(xiàn)
這篇文章主要為大家介紹了python神經(jīng)網(wǎng)絡(luò)facenet人臉檢測及keras實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Python3 使用selenium插件爬取蘇寧商家聯(lián)系電話
這篇文章主要介紹了Python3 selenium爬取蘇寧商家聯(lián)系電話,此處使用了selenium插件 使用的是火狐瀏覽器 信息存儲(chǔ)到csv表格里面,需要的朋友可以參考下2019-12-12用Python的繪圖庫(matplotlib)繪制小波能量譜
這篇文章主要介紹了用Python的繪圖庫(matplotlib)繪制小波能量譜,代碼簡單詳細(xì),思路清晰,需要的朋友可以參考下2021-04-04