裝飾器的定義
“”"
裝飾器:
把一個函數當作參數,返回一個替代版的函數
本質上就是一個返回函數的函數
“在不改變原函數的基礎上,給函數增加功能”
“”"
在企業中,別人定義好的函數我們一般是不能改動的,那麼如果我們想在這個函數的基礎上進行一些改動,裝飾器就是一個很好的工具:
比如我們定義一個函數,他的功能是打印出hello world
def fun():
print('hello world')
如果我們想讓在打印helloworld之前打印別的東西,且不改變原函數,我們可以:
#一
這樣的話如果次數太多會變的麻煩
#二:
def fun():
print('hello world')
# print('linux to linux')
# fun()
def fun1():
print('linux to linux')
fun()
fun1()
這樣的話函數名變成了fun1,調用方式改變,體現不出原函數。
#三:
def f1():
print('hello world')
#定義裝飾器
def decorator(fun):
def wrapper():
print('linux to linux')
fun()
return wrapper 這裏不加括號,加上括號代表括號的運行結果,不加代表函數本身。
f = decorator(f1) #把f1這個函數傳進去,用f調用
f()
這樣函數名也變了,也有些體現不出原函數。
#四:
python中的語法糖是專門調用裝飾器的
放置於函數的上方,以@開頭,加上定義的裝飾器函數,就可以直接調用裝飾器的函數,先運行裝飾器函數,在運行已經定義的函數。
def decorator(fun):
def wrapper():
print('linux to linux')
fun()
return wrapper
@decorator #裝飾器
def f1():
print('hello world')
f1()
練習:
已知函數:
def f1(func_name):
print('This is a function ’ + func_name)
要在打印的字符串前打印時間,要求用裝飾器:
1,
import time
def decorator(func):
def wrapper(*args): # *args 表示可以接收多個參數
print(time.time()) # 加的打印時間功能
func(*args)
return wrapper
@decorator
def f1(func_name):
print('This is a function ' + func_name)
f1 ('test')
運行,
2,是否可以接收多個參數:
import time
def decorator(func):
def wrapper(*args):
print(time.time())
func(*args)
return wrapper
@decorator
def f2(func_name1,func_name2):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
f2 ('test1','test2')
可以接受多個參數。
3,關鍵字參數
import time
def decorator(func):
def wrapper(*args,**kwargs): **kwargs代表接收關鍵字參數
print(time.time())
func(*args,**kwargs)
return wrapper #這是標準的裝飾器寫法
@decorator
def f3(func_name1,func_name2,**kwargs):
print('This is a function ' + func_name1)
print('This is a function ' + func_name2)
print(kwargs)
f3('test1','test2',a=1,b=2,c='hello')
練習二:
“”"
用 裝飾器實現一個函數計時器
“”"
函數計時器就是在函數前後加上時間,然後兩個在一相減。
import time
def timetest(fun):
def wrapper(*args,**kwargs):
start_time = time.time()
fun(*args,**kwargs)
end_time = time.time()
print('the running time is: %.6f' %(end_time - start_time))
return wrapper
@timetest
def fun_list(n):
return [2 * i for i in range(n)]
#@timetest
#def fun_map(n):
# return list(map(lambda x:x*2 ,range(n)))
print(fun_list(10000)) 單純執行函數只是執行函數裏面的內容,所以要print打印出來
但是這樣的執行結果是這樣的:
因爲在執行裝飾器函數的時候,會先進入裝飾器函數中,fun(*args,**kwargs) 這個代碼只是運行了對應的 fun_list() 函數的內容,缺少了對其運行結果的處理,所以應定義返回值,wrapper 中並沒有返回值,返回值對應的是執行結果,只有定義了返回值,纔可以看到執行結果。
import time
def timetest(fun):
def wrapper(*args,**kwargs):
start_time = time.time()
res = fun(*args,**kwargs) #這一行
end_time = time.time()
print('the running time is: %.6f' %(end_time - start_time))
return res #這一行
return wrapper
@timetest
def fun_list(n):
return [2 * i for i in range(n)]
#@timetest
#def fun_map(n):
# return list(map(lambda x:x*2 ,range(n)))
print(fun_list(10000))
執行:
functools工具包中的wraps裝飾器作用及用法
在他人使用函數式,爲了使使用者易於讀懂,會使用wraps工具包保留原函數名和幫助信息文檔,通常卸載wrapper函數上方,用法如下:
import time
import functools
def timetest(fun):
"""This is a decorator fun"""
@functools.wraps(fun) 不加這條命令會打印出 wrapper 的幫助文檔和函數名
def wrapper(*args,**kwargs):
"""This is a wrapper function"""
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('the running time is: %.6f' %(end_time - start_time))
return res
return wrapper
@timetest
def fun_list(n):
"""This is the fun_list function""" 幫助文檔
return [2 * i for i in range(n)]
# print(fun_list(10000))
print(fun_list.__doc__)
print(fun_list.__name__)
這時使用 print(fun_list.doc) 命令就可以看見原函數的幫助文檔
使用 print(fun_list.name) 命令就可以看見原函數名
練習二:
創建裝飾器, 要求如下:
- 創建add_log裝飾器, 被裝飾的函數打印日誌信息;
- 日誌格式爲: [字符串時間] 函數名: xxx, 運行時間:xxx, 運行返回 值結果:xxx
import time
import functools
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('[%s] 函數名: %s, 運行時間: %.6f,運行返回值結果: %d' %(time.ctime(),fun.__name__,end_time - start_time,res) )
return res
return wrapper
@add_log
def add(x,y):
time.sleep(1)
return x+y
add(1,10)
執行結果:
##判斷參數是不是一個類型:
用 isinstance 判斷一個參數是不是後面跟的數據類型。
練習三:帶參數的裝飾器
編寫裝飾器required_types, 條件如下:
1). 當裝飾器爲 @required_types(int,float) 確保函數接收到的每一>個參數都是int或者float類型; 2). 當裝飾器爲 @required_types(list) 確保函數接收到的每一個參數 都是list類型; 3). 當裝飾器爲 @required_types(str,int) 確保函數接收到的每一個>參數都是str或者int類型; 4). 如果參數不滿足條件, 打印 TypeError:參數必須爲xxxx類型
import functools
def required_types(*kind): #這個函數用來接收數據類型
def required(fun): #這個函數用來導入函數
@functools.wraps(fun)
def wrapper(*args,**kwargs): #裝飾器函數
for i in args:
if not isinstance(i,kind):
print('TypeError: 參數必須爲%s,%s類型' %kind)
exit()
else:
res = fun(*args,**kwargs)
return res
return wrapper
return required
@required_types(float,float) #括號內的參數爲數據類型
def add(a,b):
return a + b
print(add(1.2,2.1))
兩個數據都是浮點型時執行結果正常。
數據類型不符合裝飾器參數時報錯
練習四:多個裝飾器
#裝飾器1
def decorator_a(fun):
def inner_a(*args,**kwargs):
print('This is inner_a')
res = fun(*args,**kwargs)
return res
return inner_a
#裝飾器2
def decorator_b(fun):
def inner_b(*args,**kwargs):
print('This is inner_b')
b = fun(*args,**kwargs)
return b
return inner_b
#都放在函數上面,會按順序執行附加功能
@decorator_b
@decorator_a
def f(x):
print('This is f')
return x*2
print(f(1))
可以看出先執行了裝飾器 b ,因爲它放在上面。