python單例模式

2021-10-04 11:30:13 字數 4351 閱讀 6245

1、什麼情況下使用單例模式

當每個例項都會占用資源, 而且例項初始化會影響效能, 這個時候可以考慮使用單例模式, 好處就是只例項化一次, 只需要初始化一次

當有同步需要的時候, 可以通過乙個例項來進行同步控制, 比如對某個共享檔案(如 日誌檔案)的控制, 對計步器的控制

2、裝飾器實現單例模式

def

singleton

(cls)

:#例項字典

instance_dict =

@wraps(cls)

# 防止使用裝飾器後,函式名的改變

defnew_cls

(*args,

**kwargs)

:#獲得源類的屬性,對類名、引數列表都一樣,只建立乙個這樣的例項

key =

'{}_{}_{}'

.format

(cls.__name__,args,kwargs)

print

(key,cls(

*args,

**kwargs)

)print

(cls)

if key not

in instance_dict:

instance_dict[key]

= cls(

*args,

**kwargs)

return instance_dict[key]

return new_cls

def singleton(cls),引數是cls(當前類的具體型別),可以來呼叫類的屬性,類的方法,例項化物件等

@wraps(cls),在使用裝飾器裝飾函式以後,被裝飾的函式其實已經是另外乙個函式了(函式名等函式屬性會發生改變),當同一裝飾器需要裝飾多個函式時,會導致被修飾的函式全部變為同乙個函式名,執行**必然會報錯。為了不影響,python中的functools包中提供了乙個叫wraps的decorator來消除這樣的***,使用@wraps後,被裝飾的函式函式名保持不變

這裡利用了字典元素的特性,乙個鍵只有乙個對應的值,如果由類名、類引數列表組成的字串鍵在instance_dict字典中不存在,則建立這個鍵值對,如果鍵存在,則不進行操作,然後把值(類例項)返回。這樣就做到了,如果類名、類引數列表都一樣,則只能建立乙個例項。在createlog類名上加上@singleton:

p1 = createlog(

)p2 = createlog(

)print(id

(p1)

==id

(p2)

)

結果為true,說明單例成功

3、使用類裝飾器實現單例模式

class

singleton

(object):

def__init__

(self, cls)

: self._cls = cls

self._instance =

def__call__

(self)

:if self._cls not

in self._instance:

self._instance[self._cls]

= self._cls(

)return self._instance[self._cls]

@singleton

class

cls2

(object):

def__init__

(self)

:pass

cls1 = cls2(

)cls2 = cls2(

)print(id

(cls1)

==id

(cls2)

)

4、使用new、metaclass 關鍵字

元類(metaclass) 可以通過方法metaclass創造了類(class),而類(class)通過方法new創造了例項(instance)。

在單例模式應用中,在創造類的過程中或者創造例項的過程中稍加控制達到最後產生的例項都是乙個物件的目的

4.1、使用new關鍵字實現單例模式

使用new方法在創造例項時進行干預,達到實現單例模式的目的,這種方式需要在每個需要使用單例模式的類重寫__new__()方

class

single

(object):

_instance =

none

#這是用來儲存例項物件的類屬性

def__new__

(cls,

*args,

**kw)

:if cls._instance is

none

: cls._instance =

object

.__new__(cls,

*args,

**kw)

# __new__在object類中是靜態方法

return cls._instance # 返回這個類的例項

def__init__

(self)

:pass

single1 = single(

)single2 = single(

)print(id

(single1)

==id

(single2)

)

new() 方法和init() 方法:

new()方法的第乙個引數是cls,是這個類;init() 方法的第乙個引數是self,是類的例項物件。new()方法第乙個執行,先於init() 方法

new()方法返回值是乙個例項物件,只有__new__()方法執行完後,init() 方法才能開始工作。即__new__()的返回值是__init__()中的slef

object類中將__new__()定義為靜態方法,因此可以使用object.new(cls, *args, **kw)

4.2 用 metaclass 實現單例模式

同new關鍵字一樣,使用metaclass關鍵字實現單例模式,只需要在建立類時進行干預

type函式動態建立類

要動態建立乙個class物件,type函式需要傳入三個引數

class名稱,字串形式

繼承的父類的集合,python支援多繼承,要注意多個父類的元祖表達形式

要建立的class名稱與方法的繫結

hello = type(『hello』,(object,),dict(hello=fn))

類名為hello,繼承自object類,hello類中有方法叫hello,這個方法指向fn

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

cls4

(metaclass=singleton)

:pass

cls1 = cls4(

)cls2 = cls4(

)print(id

(cls1)

==id

(cls2)

)

這種方式不需要為每個類單獨建立單例模式,只需要將元類重寫

5、單例模式的使用場景

python 的logger 就是乙個單例模式, 用以日誌記錄

windows 的資源管理是乙個單例模式

windows中只有乙個**站,開啟一次**站只會出現一次視窗,雙擊第二次不會出現第二個視窗 ,這就是單例,物件不會重新建立,只會建立一次

簡訊驗證碼

建立物件去傳送簡訊,如果有幾十萬個使用者同時有傳送簡訊的需求,那麼該sdk就要重複建立幾十萬個用於傳送簡訊驗證碼的物件,實際上它只是要呼叫物件的方法進行傳送簡訊罷了,因此可以用單例設計模式減輕伺服器負擔

資料庫連線池等資源池也是用單例模式

**資料統計

python單例模式繼承 python單例模式

我們可以使用 new 這個特殊方法。該方法可以建立乙個其所在類的子類的物件。更可喜的是,我們的內建 object 基類實現了 new 方法,所以我們只需讓 sing 類繼承 object 類,就可以利用 object 的 new 方法來建立 sing 物件了。classsing object def...

單例模式 python

單例模式 保證乙個類僅有乙個例項,並提供乙個訪問它的全域性訪問點。實現 某個類只有乙個例項 途徑 1 讓乙個全域性變數使得乙個物件被訪問,但是它不能防止外部例項化多個物件。2 讓類自身負責儲存它的唯一例項。這個類可以保證沒有其他例項可以被建立。即單例模式。多執行緒時的單例模式 加鎖 雙重鎖定。餓漢式...

python單例模式

new 在 init 之前被呼叫,用於生成例項物件。利用這個方法和類的屬性的特點可以實現設計模式的單例模式。單例模式是指建立唯一物件,單例模式設計的類只能例項 例項化1個物件。class singleton object instance none def init self pass def ne...