裝飾器基礎
前面快速介紹了裝飾器的語法,在這裡,我們將深入裝飾器內部工作機制,更詳細更系統地介紹裝飾器的內容,並學習自己編寫新的裝飾器的更多高階語法。
什麼是裝飾器
裝飾是為函式和類指定管理**的一種方式。python裝飾器以兩種形式呈現:
函式裝飾器在函式定義的時候進行名稱重繫結,提供乙個邏輯層來管理函式和方法或隨後對它們的呼叫。
類裝飾器在類定義的時候進行名稱重繫結,提供乙個邏輯層來管理類,或管理隨後呼叫它們所建立的例項。
簡而言之,裝飾器提供了一種方法,在函式和類定義語句的末尾插入自動執行的**——對於函式裝飾器,在def的末尾;對於類裝飾器,在class的末尾。這樣的**可以扮演不同的角色。
裝飾器提供了一些和**維護性和審美相關的有點。此外,作為結構化工具,裝飾器自然地促進了**封裝,這減少了冗餘性並使得未來變得更容易。
函式裝飾器
通過在乙個函式的d語句的末尾執行另乙個函式,把最初的函式名重新繫結到結果。
用法裝飾器在緊挨著定義乙個函式或方法的def語句之前的一行編寫,並且它由@符號以及緊隨其後的對於元函式的乙個引用組成——這是管理另乙個函式的乙個函式(或其他可呼叫物件)。
在編碼上,函式裝飾器自動將如下語法:
@decorator
def f(arg):
...f(99)
對映為這個對等形式:
def f(arg):
...f = decorator(f)
f(99)
這裡的裝飾器是乙個單引數的可呼叫物件,它返回與f具有相同數目的引數的乙個可呼叫物件。
當隨後呼叫f函式的時候,它自動呼叫裝飾器所返回的物件。
換句話說,裝飾實際把如下的第一行對映為第二行(儘管裝飾器只在裝飾的時候執行一次)
fun(6,7)
decorator(func)(6,7)
這一自動名稱重繫結也解釋了之前介紹的靜態方法和property裝飾器語法的原因:
class c:
@staticmethod
def meth(...):...
@property
def name(self):...
實現裝飾器自身是返回可呼叫物件的可呼叫物件。實際上,它可以是任意型別的可呼叫物件,並且返回任意型別的可呼叫物件:函式和類的任何組合都可以使用,儘管一些組合更適合於特定的背景。
有一種常用的編碼模式——裝飾器返回了乙個包裝器,包裝器把最初的函式保持到乙個封閉的作用域中:
def decorator(f):
def wrapper(*args):
# 使用 f 和 *args
# 呼叫原來的f(*args)
return wrapper
@decorator
def func(x,y):
...func(6,7)
當隨後呼叫名稱func的時候,它確實呼叫裝飾器所返回的包裝器函式;隨後包裝器函式可能執行最初的func,因為它在乙個封閉的作用域中仍然可以使用。
為了對類做同樣的事情,我們可以過載呼叫操作:
class decorator:
def __init__(self,func):
self.func = func
def __call__(self,*args):
# 使用self.func和args
# self.fnhxzajgonunc(*args)呼叫最初的func
@decorator
def func(x,y):
...func(6,7)
但是,要注意的是,基於類的**中,它對於攔截簡單函式有效,但當它應用於類方法函式時,並不很有效:
如下反例:
class decorator:
def __init__(self,func):
self.func = func
def __call__(self,*args):
# 呼叫self.func(*args)失敗,因為c例項引數無法傳遞
class c:
@decorator
def method(self,x,y):
...這時候裝飾的方法重繫結到乙個類的方法上,而不是乙個簡單的函式,這一點帶來的問題是,當裝飾器的方法__call__隨後執行的時候,其中的self接受裝飾器類例項,並且類c的例項不會包含到乙個*args中。
這時候,巢狀函式的替代方法工作得更好:
def decorator:
def warpper(*args):
# ...
return wrapper
@decorator
def func(x,y):
...func(6,7)
class c:
@decorator
def method(self,x,y):
...x = c()
x.method(6,7)
類裝飾器
類裝飾器與函式裝飾器使用相同的語法和非常相似的編碼方式。類裝飾器是管理類的一種方式,或者用管理或擴充套件類所建立的例項的額外邏輯,來包裝例項構建呼叫。
用法假設類裝飾器返回乙個可呼叫物件的乙個單引數的函式,類裝飾器的語法為:
@decorator
class c:
...x = c(99)
等同於下面的語法:
class c:
...c = decorator(c)
x = c(99)
直接效果是隨後呼叫類名會建立乙個例項,該例項會觸發裝飾器所返回的可呼叫物件,而不是呼叫最初的類自身。
實現類裝飾器返回的可呼叫物件,通常建立並返回最初的類的乙個新的例項,以某種方式來擴充套件對其介面的管理。例如,下面的例項插入乙個物件來攔截乙個類例項的未定義的屬性:
def decorator(cls):
class wrapper:
def __init__(self,*args):
self.wrapped = cls(*args)
def __getattr__(self,name):
return getattr(self.wrapped,name)
return wrapper
@decorator
class c: # c = decorator(c)
def __init__(self,x,y): # run by wrapper.__init__
self.attr = 'spam'
x = c(6,7) # 等價於wrapper(6,7)
print(x.attr)
在這個例子中,裝飾器把類的名稱重新繫結到另乙個類,這個類在乙個封閉的作用域中保持了最初的類。
就像函式裝飾器一樣,類裝飾器通常可以編寫為乙個建立並返回可呼叫物件的「工廠」函式。
裝飾器巢狀
有時候,乙個裝飾器不夠,裝飾器語法允許我們向乙個裝飾器的函式或方法新增包裝器邏輯的多個層。這種形式的裝飾器的語法為:
@a@b@c
def f(...):
...如下這樣轉換:
def f(程式設計客棧...):
...f = a(b(c(f)))
這裡,最初的函式通過3個不同的裝飾器傳遞,每個裝飾器處理前乙個結果。
裝飾器引數
函式裝飾器和類裝飾器都能接受引數,如下:
@decorator(a,b)
def f(arg):
...f(99)
自動對映到其對等形式:
def f(arg):
...f = decorator(a,b)(f)
f(99)
裝飾器引數在裝飾之前就解析了,並且它們通常用來保持狀態資訊供隨後的呼叫使用。例如,這個例子中的裝飾器函式,可能採用如下形式:
def decorator(a,b):
# 儲存或使用a和b
def actualdecorator(f):
# 儲存或使用函式 f
# 返回乙個可呼叫物件
return callable
return actualdecorator
以上,這是裝飾器的nhxzajgon基礎知識,接下來將學習編寫自己的裝飾器
python基礎 裝飾器
裝飾器本質就是函式,功能是為其他函式新增附加功能。原則 不修改被修飾函式的源 不修改被修飾函式的呼叫方式 裝飾器的知識儲備 裝飾器 高階函式 函式巢狀 閉包 import time 定義乙個裝飾器計算函式執行時間 def timer func start time time.time res fun...
python基礎 裝飾器
裝飾器形成的過程 最簡單的裝飾器 有返回值的 有乙個引數 萬能引數 裝飾器的作用 原則 開放封閉原則 語法糖 裝飾器的固定模式 import time print time.time 獲取當前時間 time.sleep 10 讓程式在執行到這個位置的時候停一會兒 def timmer f 裝飾器函式...
Python基礎 裝飾器
裝飾器是程式開發中經常會用到的乙個功能,程式開發的基礎知識,用好了裝飾器,開發效率如虎添翼,所以這也是python面試中必問的問題,但對於好多初次接觸這個知識的人來講,這個功能有點繞,這個都不會,別跟人家說你會python,看了下面的文章,保證你學會裝飾器。裝飾器 decorator 首先我們需要知...