標籤:自己的 type rgs 順序儲存 應用 recent 賦值 tee 變數
介紹關於類的一些進階主題,這些是可選的,在Python應用程式中,不會常常遇到。
==========================================================================
slots執行個體
將字串屬性名稱順序賦值給特殊的__slots__類屬性。就能夠限制類的執行個體將有的合法屬性集。
這個特殊屬性通常是在class語句頂層內將字串名稱順序賦值給變數__slots__而設定:僅僅有__slots__列表內的這些變數名可賦值為執行個體屬性。而執行個體屬性名稱必須在引用前賦值,即使是列在__slots__中也是這樣。看下述範例:
>>> class limiter(object):__slots__ = [‘age‘,‘name‘,‘job‘]>>> x = limiter()>>> x.ageTraceback (most recent call last): File "<pyshell#4>", line 1, in <module> x.ageAttributeError: age>>> x.age = 40>>> x.age40>>> x.ape = 1000Traceback (most recent call last): File "<pyshell#7>", line 1, in <module> x.ape = 1000AttributeError: ‘limiter‘ object has no attribute ‘ape‘
假設建立了非常多執行個體而且僅僅有幾個屬性是必需的話。那麼為每一個執行個體對象分配一個命名空間字典可能在記憶體方面的代價過於昂貴。要節省空間的和運行速度,slot屬效能夠順序儲存以供高速尋找,而不是為每一個執行個體分配一個字典。
-------------------------------------------------------------------------------------------------------------------------------------
Slot和通用代碼
實際上。有些帶有slots的執行個體或許根本就沒有__dict__屬性字典。所以在有些程式中要使用比__dict__更為儲存中立的工具,比如getattr、setattr和dir內建函數。
>>> class C:__slots__ = [‘a‘,‘b‘]>>> X = C()>>> X.a = 1>>> X.a1>>> X.__dict__Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> X.__dict__AttributeError: ‘C‘ object has no attribute ‘__dict__‘>>> getattr(X,‘a‘)1>>> setattr(X,‘b‘,2)>>> X.b2>>> ‘a‘ in dir(X)True>>> ‘b‘ in dir(X)True
沒有屬性命名空間字典,不可能給不是slots列表中名稱的執行個體來分配新的名稱,然而,通過在__slots__中包括__dict__仍然能夠容納額外的屬性,從而考慮到一個屬性空間字典的須要。在這個範例中。兩種機制都用到了,可是,getattr這種通用工具同意我們將它們當做單獨一組屬性對待:
>>> class D:__slots__ = [‘a‘,‘b‘,‘__dict__‘]c = 3def __init__(self):self.d = 4>>> X = D()>>> X.d4>>> X.__dict__{‘d‘: 4}>>> X.__slots__[‘a‘, ‘b‘, ‘__dict__‘]>>> X.c3>>> X.aTraceback (most recent call last): File "<pyshell#24>", line 1, in <module> X.aAttributeError: a>>> X.a = 1>>> getattr(X,‘a‘),getattr(X,‘c‘),getattr(X,‘d‘)(1, 3, 4)
==========================================================================
類特性
有一種稱為特性(property)的機制,提供還有一種方式讓類定義自己主動調用的方法,來讀取或賦值執行個體屬性。
【特性和slots都是基於屬性描寫敘述器的新概念】
簡而言之,特性是一種對象,賦值給類屬性名稱。特性的產生是以三種方法(獲得、設定以及刪除運算的處理器)以及通過文檔字串調用內建函數property。假設不論什麼參數以None傳遞或者省略。該運算就不能支援。
特性一般都是在class語句頂層賦值【比如,name = property(...)】。這樣賦值時。對類屬性本身的讀取(比如。obj.name),就會自己主動傳給property的一個讀取方法。例如以下例:
>>> class new():def getage(self):return 40age = property(getage,None,None,None) # get,set,del,docs>>> x = new()>>> x.age40>>> x.nameTraceback (most recent call last): File "<pyshell#34>", line 1, in <module> x.nameAttributeError: ‘new‘ object has no attribute ‘name‘
特性比傳統技術執行起來更快。
比如,當我們新增屬性賦值運算支援時,特性就變得更有吸引力:輸入的代碼更少,對我們不希望動態計算的屬性進行賦值運算時,不會發生額外的方法調用。
>>> class new():def getage(self):return 40def setage(self,value):print(‘set age:‘,value)self._age = valueage = property(getage,setage,None,None)>>> x = new()>>> x.age40>>> x.age = 42set age: 42>>> x.age40>>> x._age42>>> x.job = ‘trainer‘>>> x.job‘trainer‘
==========================================================================
__getattribute__和描寫敘述符
__getattribute__能夠讓類攔截全部屬性的引用。而不局限於沒有定義的引用(假設__getattr__)。
除了特性和運算子多載方法,Python支援屬性描寫敘述符的概念——帶有__get__和__set__方法的類,分配給類屬性而且由執行個體繼承,這攔截了對特定屬性的讀取和寫入訪問。描寫敘述符在某種意義上是特性的一種更加通用的形式。
關於特性、__getattribute__和描寫敘述符這些進階話題將在興許逐步介紹。
==========================================================================
靜態方法
類方法通常在其第一個參數中傳遞一個執行個體對象。以充當方法調用的一個隱式主體。有時候。程式須要處理與類而不是與執行個體相關的資料,也就是我們須要一個類中的方法不僅不傳遞並且也不期待一個self執行個體參數。
Python通過【靜態方法】的概念來支援這種目標——嵌套在一個類中的沒有self參數的簡單函數。
比如。如果我們想使用類屬性去計算從一個類產生了多少執行個體。我們能夠把一個計數器儲存為類屬性。每次建立一個新的執行個體的時候。建構函式都會對計數器加1。而且,有一個顯示計數器值的方法。【記住,類屬性是由全部執行個體共用的】:
>>> class Spam:numInstances = 0def __init__(self):Spam.numInstances += 1def printNumInstances():print(‘Number of instances created:‘,Spam.numInstances)
printNumInstances方法旨在處理類資料而不是執行個體資料——它是關於全部執行個體的,而不是某個特定的執行個體。因此,我們想要不必傳遞一個執行個體就能夠調用它。
實際上,我們不想產生一個執行個體來擷取執行個體的數目。由於這本身就會改變我們想要擷取的執行個體的數目。
換句話說:我們想要一個無self的靜態方法。
我們能夠看一下測試結果:
>>> a = Spam()>>> b = Spam()>>> c = Spam()>>> Spam.printNumInstances()Number of instances created: 3>>> a.printNumInstances()Traceback (most recent call last): File "<pyshell#66>", line 1, in <module> a.printNumInstances()TypeError: printNumInstances() takes 0 positional arguments but 1 was given
能夠看到,我們通過類調用無self方法執行成功。而通過執行個體調用無self方法出錯了,由於參數不正確。通過一個執行個體調用方法。這個執行個體會自己主動傳遞給方法的第一個參數中,可是這個無self方法並不存在參數來接收它。
假設你可以堅持僅僅通過類調用無self方法。那麼事實上你已經有了一個靜態方法了。然而假設你還想通過執行個體調用,那麼就須要採取其它設計,或者把這個方案標記為特殊的。
-------------------------------------------------------------------------------------------------------------------------------------
使用靜態方法
要將這個無self方法標記為靜態方法。須要調用內建函數staticmethod。例如以下:
>>> class Spam:numInstances = 0def __init__(self):Spam.numInstances += 1def printNumInstances():print(‘Number of instances created:‘,Spam.numInstances)printNumInstances = staticmethod(printNumInstances) # staticmethod>>> a = Spam()>>> b = Spam()>>> c = Spam()>>> a.printNumInstances()Number of instances created: 3
靜態方法對於處理一個類本地的資料來說是更好的解決方式。
==========================================================================
裝飾器和元類:初識
上邊提到的staticmethod可能比較奇怪。所以新增了一個功能,要讓這個運算變得簡單。【函數裝飾器】提供了一種方式,替函數明白了特定的運算模式。也就是將函數包裹了還有一層,在還有一函數的邏輯內實現。
函數裝飾器變成了通用的工具:除了靜態方法使用方法外。也可用於新增多種邏輯的函數。比如,能夠用來記錄函數調用的資訊和在出錯時檢查傳入的參數類型等
Python提供了一下內建的函數裝飾器,來做一些運算,比如。標識靜態方法,可是我們也能夠編寫自己的隨意裝飾器。儘管不限於使用類,但使用者定義的函數裝飾器通常也寫成類,把原始函數和其它資料當成狀態資訊。
-------------------------------------------------------------------------------------------------------------------------------------
函數裝飾器基礎
從文法上講,函數裝飾器是它後邊的函數的執行時的聲明。
函數裝飾器是寫成一行,就在定義函數或方法的def語句之前,並且由@符號、後面跟著所謂的元函數組成:也就是管理還有一函數的函數。
比如,如今的靜態方法我們能夠用下述的裝飾器文法編寫:
class C:@staticmethoddef printNumInstances():...
這個文法和以下的寫法有同樣的效果:
class C:def meth():...meth = staticmethod(meth)
結果就是,調用方法函數的名稱。實際上是觸發了它staticmethod裝飾器的結果。
-------------------------------------------------------------------------------------------------------------------------------------
裝飾器範例
之前介紹過,__call__運算子多載方法為類執行個體實現函數調用介面。
以下的程式通過這樣的方法定義類。在執行個體中儲存裝飾的函數,並捕捉對最初變數名的調用。
class tracer: def __init__(self,func): self.calls = 0 self.func = func def __call__(self,*args): self.calls += 1 print(‘call %s to %s‘%(self.calls,self.func.__name__)www.aomenyonli.cn) self.func(*args)@tracerdef spam(a,b,c): print(a,b,c)spam(1,2,3)spam(‘a‘,‘b‘,‘c‘)spam(4,5,6)
由於spam函數時通過tracer裝飾器啟動並執行,所以當最初的變數名spam調用時。實際上觸發的是類中的__call__方法,這種方法會計算和記錄該次調用,然後託付給原始的包裹的函數。
因此,此裝飾器可用於包裹攜帶隨意數目參數的不論什麼函數。
結果就是新增一層邏輯至原始的spam函數。執行結果例如以下:第一行來自tracer類,第二行來自spam函數。
call 1 to spam1 2 3call 2 to spama b ccall 3 to spam4 5 6
這裡僅僅是初步瞭解。興許我們將會介紹各種各種的方法來編寫函數裝飾器。
-------------------------------------------------------------------------------------------------------------------------------------
類裝飾器和元類
類裝飾器類似於函數裝飾器,可是。它們在一條class語句的末尾執行,而且把一個類名又一次綁定到一個可調用對象,相同的,它們能夠用來管理類,或者當隨後建立執行個體的時候插入一個封裝邏輯層來管理執行個體。代碼結構例如以下:
def decorator(aClass):...@decoratorclass C:...
被映射為下列相當代碼:
def decorator(aClass):...class C:...C = decorator(C
6788878.cnwww.senta77.com)
元類是一種類似的基於類的進階工具。其用途往往與類裝飾器有所重合。
它們提供了一種可選的模式。會把一個類對象的建立導向到頂級type類的一個子類。
關於裝飾器和元類的內容,將在之後更加具體地介紹。
Python collections模組總結