按照這個順序去循序漸進理解裝飾器:高階函式->柯里化->裝飾器
在python裡,函式可以是物件,可以作為普通變數、引數、返回值等等。
所以高階函式定義:
1.輸入乙個或多個函式作為引數;
2.輸出乙個函式。
例如樣式:
y = g(f(x))
閉包就是乙個高階函式:
defcounter(base):
def inc(step=1):
nonlocal base
#注意這裡要使用nonlocal關鍵字,參見函式作用域那一篇
base +=step
return
base
return inc
python內建的很多函式都是高階函式,例如:sorted(),filter(),map()函式等。
sorted():
>>> l1 = [0,1,2,3,4]>>> sorted(l1, key = lambda x:x+6) #sorted(iterable[,key][,reverse])返回新列表
[0, 1, 2, 3, 4]
>>> l1.sort(key = lambda x:x+6) # 就地對原列表修改
>>>l1
[0, 1, 2, 3, 4]
filter():
>>> filter(lambda x:x%3 ==0, l1) # filter(function, iterable)過濾可迭代物件對元素,返回乙個生成器。將lambda下宣告的匿名函式作為引數傳進filter()>>> list(filter(lambda x:x%3 ==0, l1)) # 將生成器轉化為列表
[0, 3]
>>> temp = filter(lambda x:x%3 ==0, l1) # 輸出生成器裡的元素
>>> for i in
temp:
(i)
03
map():
>>> map(lambda x:x*2+1,range(5)) # map(function, *iterable)>>> list(map(lambda x:x*2+1,range(5))) # 轉列表
[1, 3, 5, 7, 9]
>>> dict(map(lambda x:(x%5,x),range(10))) # 轉字典,裡面有字典的key去重
柯里化currying(從高階函式開始向裝飾器過渡):
將原來接受兩個引數的函式變成新的接受乙個引數的函式的過程。新的函式返回乙個以原有第二個引數為引數的函式。
簡單表達為:z = f(x,y) ==> z = f(x)(y)
同樣是舉乙個簡單的例子:
#原函式def
add(x, y):
return x+y
# 柯里化後的函式(也是閉包)
defadd(x):
def_add(y):
return x+y
return
_add#呼叫
add(5)(6) #
輸出11
了解了以上兩個概念,我們跳轉到乙個具體的場景中:
假設在工作中遇到乙個針對add()函式的需求:增強add()函式的功能,使其能夠輸出被呼叫時的引數資訊。
#原函式def
add(x, y);
return x+y
#增加資訊輸出功能
defadd(x, y);
print("
call add, x + y
") #
日誌輸出到控制台
return x+y
我們發現上面方法雖然完成需求,但是輸出資訊的非業務功能**不該放在加法運算的業務功能**中,耦合度很高。
為了將業務功能**和非業務功能**分離開來,我們想出辦法:將兩者獨立開來,並將業務功能函式作為引數傳入非業務功能函式。
#業務功能
defadd(x ,y):
return x+y
#非業務功能
deflogger(fn):
print('
begin')
x = fn(4, 5)
print('
end'
)
returnx#
完成需求
print(logger(add))
上面的方法能很好的解決業務功能**和非業務功能**分離開來的問題,但是我們也會發現業務功能函式(被裝飾的函式)的引數在非業務功能函式(裝飾函式)的呼叫傳參時是乙個問題:
logger(fn)裡fn(4, 5)是寫死的。
為了能夠更靈活地傳參,我們使用*args,**kwargs來表示原函式的引數。
#原函式(不做任何改動)
defadd(x, y):
return x+y
#增加的非業務功能部分(將原函式和原函式裡的引數一併作為新函式的引數傳遞進去);*args和**kwargs的知識點就不在這裡展開了
def logger(fn, *args, **kwargs):
print('
begin')
x = fn(*args, **kwargs)
print('
end'
)
returnx#
呼叫print(logger(add,5,y=10))
上面的方法已經能滿足我們的需求,並解決了業務**和非業務**混在一起和引數傳遞的問題,但是我們發現在函式呼叫的時候,我們的寫法是呼叫logger()函式( logger(fn,*args,**kwargs) )來實現業務和非業務的功能。怎樣能夠是呼叫add()函式來實現功能呢?可以聯想到上面提到的柯里化方法:
logger(fn,*args,**kwargs) --柯里化--> logger(fn)(*args,**kwargs) --將logger(fn)賦給add(),呼叫方式即可以寫為 --> add(*args,**kwargs)
defadd(x, y):
return x+y
#將logger()函式柯里化
deflogger(fn):
print('
begin')
x = fn(*args, **kwargs)
print('
end'
)
return
x
return
#直接add()函式呼叫;第一句的鋪墊在下面用語法糖代替
add =logger(add)
print(add(5,y=10))
到這,就已經形成了乙個裝飾器的雛形,再加上裝飾器的語法糖和調換兩個函式的位置後就是乙個完整的裝飾器(包含裝飾函式和被裝飾函式):
deflogger(fn):
print('
begin')
x = fn(*args, **kwargs)
print('
end'
)
return
x
return
@logger
#等價於 add = logger(add);調換順序是因為在這裡要先宣告裝飾器函式,然後根據這句語法糖,裝飾緊跟在@logger後面的(被裝飾的)原函式
defadd(x, y):
return x+y#呼叫
print(add(5,y=10))
我們根據上面的形式,做乙個簡單的歸納:
裝飾器:
1.它是乙個函式(logger())。
2.被裝飾的函式(add())作為它的形參(logger(fn))。
4.語法糖:使用@function_name(裝飾器函式名)的方式簡化呼叫(@logger)。
裝飾器是高階函式,同時裝飾器起著對傳入函式的功能的裝飾作用。
python裝飾器 Python 裝飾器
簡言之,python裝飾器就是用於拓展原來函式功能的一種函式,這個函式的特殊之處在於它的返回值也是乙個函式,使用python裝飾器的好處就是在不用更改原函式的 前提下給函式增加新的功能。一般而言,我們要想拓展原來函式 最直接的辦法就是侵入 裡面修改,例如 這是我們最原始的的乙個函式,然後我們試圖記錄...
python裝飾器 裝飾器
由於函式也是乙個物件,而且函式物件可以被賦值給變數,所以,通過變數也能呼叫該函式。def now print 2015 3 25 f now f 2015 3 25 函式物件有乙個 name 屬性,可以拿到函式的名字 now.name now f.name now 現在,假設我們要增強now 函式的...
python裝飾器原理 Python裝飾器原理
裝飾器 decorator 是物件導向設計模式的一種,這種模式的核心思想是在不改變原來核心業務邏輯 的情況下,對函式或類物件進行額外的修飾。python中的裝飾器由python直譯器直接支援,其定義形式如下 decorator def core service 要理解上述 的含義,我們從自定義函式裝...