參考鏈接:Python 函數裝飾器
我認為python中的裝飾器是一個很厲害的功能,他能瞬間提升代碼的逼格,但對於我這樣的小白來說,別說為所欲為的使用了,就連簡單的嘗試一下,卻也是難於登天。經過長達半年的努力,我終於找到了大部分裝飾器的介紹信息,魯迅曾經說過,良好的開始就代表了成功的一半,在我看來,魯迅分明還是太保守,良好的開端無疑代表你已經成功了。比如接下來我們只需要Ctrl+C+V,就可以完成裝飾器的學習了,親愛的小朋友們,你們學會了嗎?
給大人看的分割線
裝飾器是用來給已經定義好的函數增加功能用的,能在方法運行期間動態增加方法的功能
1)不會改變原來的代碼結構。
為什么這么說呢,因為相比於另一種方法:只將函數作為變量傳入另一個函數從而來減少重合,使用裝飾器並不需要將函數調用改寫
#定義這個函數來為類似foo()的需要去增加loging.warn()的全部函數增添這個loging.warn()的功能
#假如有許多函數和foo()一樣,都需要增加loging.warn()這行代碼,我們就可以定義一個函數,然后將每個需要增加功能的函數作為參數傳入
#但是這樣必須在用use_logging(foo)去替換原有的foo()這就改變了函數的調用,破壞了原有的代碼結構
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func()
def foo():
print('i am foo')
use_logging(foo)
2)裝飾器在不修改函數代碼的前提下影響代碼的功能;調用python中的函數可以作為變量去傳遞,基於這一特性,我們可以構建出自己的裝飾器;
先來了解一些基礎知識:
1)我們可以將函數作為變量傳遞。要注意括號,加了括號就會運行函數。
2)在函數的內部還可以嵌套函數
def hi(name="yasoob"):
print("現在在hi()里面運行")
def greet():
return "現在在greet()里面運行"
def welcome():
return "現在在welcom()里面運行"
print(greet())
print(welcome())
print("現在你返回到了hi()里面")
hi()
# 上面展示了無論何時你調用hi(), greet()和welcome()將會同時被調用。 # 然后greet()和welcome()函數在hi()函數之外是不能訪問的,比如
結果:
現在在hi()里面運行 現在在greet()里面運行 現在在welcom()里面運行 現在你返回到了hi()里面
3)從函數中返回函數
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"
def welcome():
return "now you are in the welcome() function"
if name == "yasoob":
return greet
else:
return welcome
a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500>
#上面清晰地展示了`a`現在指向到hi()函數中的greet()函數
#現在試試這個
print(a())
#outputs: now you are in the greet() function
注意下面的 hi()返回的值是greet,加上后面的括號時,就成為了greet()隨即開始運行
hi()() #這會輸出 now you are in the greet() function。
4)將函數作為參數傳遞給另外一個函數
def hi():
return "hi yasoob!"
def doSomethingBeforeHi(func):
print("I am doing some boring work before executing hi()")
print(func())
doSomethingBeforeHi(hi) #注意不要加括號
#outputs:I am doing some boring work before executing hi()
# hi yasoob!
第一個修飾器
def a_new_decorator(a_func):
def wrapTheFunction():
print('我在a_func執行前做一些事情')
a_func()
print('我在a_func執行后做一些事情')
return wrapTheFunction #記得把這個內定義的函數返回
def a_function_requiring_decoration(): #定義一個函數,等會用修飾器來修飾
print('我是需要用修飾器修飾的函數')
#使用修飾器
a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
結果:
我在a_func執行前做一些事情 我是需要用修飾器修飾的函數 我在a_func執行后做一些事情
真正的裝飾器:decorator
另一種生成被裝飾函數的方法:用@
補充----------------
裝飾器,只能在函數運行前做一些事情,也能在函數運行后執行,不過要是有返回結果的就不能在其后執行了,因為有return語句但是可以將裝飾器寫成這樣
def log(parameter1):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kw):
s=func(*args,**kw)
print('函數%s()運行消耗時間%.11f'%(func.__name__,parameter1))
return s#前面執行,這里返回
return wrapper
return decorator
補充結束------------------
先了解一下@語句
最簡單的裝飾器
如果被裝飾的函數運行時不需要參數,我們可以這樣定義裝飾器
def metric(fn):
print('%s executed in %s ms' % (fn.__name__, 10.24))
return fn
@metric
def customize(a,b):
print("我是被裝飾的函數,運行結果%d"%a+b)
customize(1,3)
#輸出
customize executed in 10.24 ms
我是被裝飾的函數,運行結果4
把@metric放到customize()函數的定義處,相當於執行了語句:
customize=metric(customize)
由於metric()是一個decorator,返回一個函數,所以,原來的customize()函數仍然存在,只是現在同名的customize變量指向了新的函數,於是調用customize()將執行新函數,即在metric()函數中返回的wrapper中返回的customize函數。
普遍使用的裝飾器的格式
上面的只是最簡單的,我們通常使用裝飾器的時候都需要被裝飾的函數的運行結果,當被裝飾的函數需要參數時,使用上面的定義就不行了:
def a_new_decorator(a_func):
def wrapTheFunction():
print('我在a_func執行前做一些事情')
a_func()
print('我在a_func執行后做一些事情')
return wrapTheFunction #記得把這個內定義的函數返回
#不同之處在這里
@a_new_decorator
def a_function_requiring_decoration(): #定義一個函數,等會用修飾器來修飾
print('我是需要用修飾器修飾的函數')
a_function_requiring_decoration()
把@log放到now()函數的定義處,相當於執行了語句:
now = log(now)
由於log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,於是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。
wrapper()函數的參數定義是(*args, **kw),因此,wrapper()函數可以接受任意參數的調用。在wrapper()函數內,首先打印日志,再緊接着調用原始函數。
解決被修飾的函數會被重命名問題的wraps函數
使用上面會出現一個問題,獲取_name_時會返回被修飾器內置函數的名字
print(a_function_requiring_decoration.__name__) # Output: wrapTheFunction 應該返回a_function_requiring_decoration
之所以返回的是修飾器里內置的名字,是因為它會重寫函數的名字和注釋文檔
而python提供了 functools.wraps() 來解決這個辦法,以如下代碼為例說明 functolls.wraps 使用方法
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
---snip---
return a_func
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
---snip--
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration
@wraps 接受一個被修飾函數的名字,並加入了復制函數名稱、注釋文檔、參數列表等功能,這讓我們能在修飾器里訪問函數被修飾之前的屬性
怎樣為修飾器傳入參數:
假如 a_function_requiring_decoration() 需要參數
def a_function_requiring_decoration(name):
--snip--
我們需要在定義 wrapTheFunction() 時指定參數即:
def wrapTheFunction(name):
---snip---
return a_func(name)
當裝飾器不知道要裝飾的函數有多少個參數時,可以用 *args 來代替
def wrapTheFunction(*args):
---snip---
return a_func(*args)
當需要關鍵字參數時
def a_function_requiring_decoration(name, age=None, height=None):
print("I am %s, age %s, height %s" % (name, age, height))
可以這樣
def wrapTheFunction(*args, **kwargs):
# args是一個數組,kwargs一個字典
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
用法
@log('execute')
def now():
print('2015-3-25')
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
>>> now = log('execute')(now)
我們來剖析上面的語句,首先執行log('execute'),返回的是decorator函數,再調用返回的函數,參數是now函數,返回值最終是wrapper函數。
原文鏈接 >>>點擊<<<
