天天看點

@kafkalistener中id的作用_python中的單例模式

@kafkalistener中id的作用_python中的單例模式

python

一、單例模式

單例模式(Singleton Pattern)是一種常用的軟體設計模式,該模式的主要目的是確定某一個類隻有一個執行個體存在。當你希望在整個系統中,某個類隻能出現一個執行個體時,單例對象就能派上用場。

在 Python 中,我們可以用多種方法來實作單例模式:

① 使用子產品

② 使用__new__方法

③ 使用裝飾器(decorator)

④ 使用元類(metaclass) (在這裡我們暫時不介紹這樣方式)

①使用子產品

其實,Python 的子產品就是天然的單例模式,因為子產品在第一次導入時,會生成 .pyc 檔案,當第二次導入時,就會直接加載 .pyc 檔案,而不會再次執行子產品代碼。是以,我們隻需把相關的函數和資料定義在一個子產品中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:

#sigle.pyclass Single(object): def instance(self): passsingle = Single()
           

将上面的代碼儲存為single.py檔案,然後如下使用:

from single import single #引入single.instance()
           

②使用__new__

__init__不是Python對象的構造方法,__init__隻負責初始化執行個體對象,在調用__init__方法之前,會首先調用__new__方法生成對象,可以認為__new__方法充當了構造方法的角色。是以可以在__new__中加以控制,使得某個類隻生成唯一對象。具體實作時可以實作一個父類,重載__new__方法,單例類隻需要繼承這個父類就好。

class Single(object): def __new__(cls,*args,**kw): # args參數前面加*表示的是任意個數的參數 # kw參數前面加**表示的是關鍵字參數,例如a = 3這種形式 #hasattr()判斷一個對象裡面是否有name屬性或者name方法,傳回BOOL值,有name特性傳回True,否則傳回False。  if not hasattr(cls,'_instance'): cls._instance = super(Single,cls).__new__(cls,*args,**kw) return cls._instance class Foo(Single): # 繼承Single類 a = 3a = Foo() #執行個體化b = Foo()print(id(a)) #id()函數是參看目前傳入的數的存放位址print(id(b))#以上結果我們會看到,a和b屬于同一個位址,實作了單例
           

注:如果要得到目前類的執行個體,應當在目前類中的__new__()方法語句中調用目前類的父類 的__new__()方法(這也是預設的),也就是說除了利用object類的方法,也可選擇其他的類來構造執行個體,但是這樣的對象也就變成了其他類的執行個體了。(其實我也想知道為什麼這樣規定)

③使用裝飾器

裝飾器維護一個字典對象instances,存了所有單例類,隻要單例不存在則建立,已經存在直接傳回該執行個體對象。

from functools import wrapsdef Single2(cls): instance = {}  @wraps(cls) #消除裝飾器的副作用 def get_instance(*args,**kw): if cls not in instance: instance[cls] = cls(*args,**kw) return instance[cls]  return get_instance @Single2 #使用python中的文法糖(@)來給類添加裝飾器class MyClass(): a = 3c = MyClass()d = MyClass()print(id(c))print(id(d))
           

注:裝飾器還是存在一定的副作用的,被裝飾的對象已經是新的對象了(python萬物皆對象),那他的元屬性也就不見了。是以,python标準庫的functools包提供了一個解決辦法,用來消除這個副作用,也就是上例中的@wraps(cls)。如果我們不加這個,就會發現被裝飾的MyClass它的元屬性都變成了wrapper函數(裝飾器最終傳回的那個函數)的資訊,如我們要是把@wraps(cls)去掉,我們print(MyClass.a)會發現沒有列印出a的值,反而會報錯。