Python學習筆記__4.4章 裝飾器(添加額外功能)
1、概覽
裝飾器可以幫助我們為已經存在的對象添加額外的功能
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。
裝飾器經常用於有切面需求的場景,比如:插入日誌、性能測試、事物處理、緩存、權限校驗等場景。
1.1、為now函數 加一行日誌
# 定義now函數
def now():
print('2018-5-8')
# 編輯decorator
def log(func): # 接受函數名
def wrapper(*args,**kw): #
print('the function name is %s():' % func.__name__) # 打印函數的__name__屬性,註意__name__ 是兩條_(下劃線)
return func(*args,**kw) # 返回這個函數
return wrapper
# 將decorator 加到 now函數
@log
def now():
print('2018-5-8')
把@log放到now()函數的定義處,相當於執行了 now=log(now)。過程為【wapper=log(now),weapper返回now()】
由於log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,於是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。
1.2、三層嵌套的decorator
如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數。比如,要自定義log的文本
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
# 添加到now() 函數
@log('execute')
def now():
print('2015-3-25')
# 效果
>>> now()
execute now():
2015-3-25
三層嵌套的執行效果:now = log('execute')(now)
1.3、原始函數的屬性復制
從上面的例子可以看出,now()函數 最終是由wrapper 函數返回的。 此時now() 函數的__name__屬性已經變為了 'wrapper'
>>> now.__name__
'wrapper'
但我們的本意只是,給now() 函數加額外功能,並不想改變它本身的屬性,此時,就需要把now() 函數的屬性復制給 wrapper() 函數
不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是幹這個事的
functools.wraps:wraps 本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器函數中。這使得裝飾器函數也有和原函數一樣的元信息
# 順便復制屬性的decorator
import functools
def log(func):
@functools.wraps(func) #這個裝飾器始終在 wrapper 函數上面
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
2、例子
1、請設計一個decorator,它可作用於任何函數上,並打印該函數的執行時間:
# -*- coding: utf-8 -*-
import time, functools
def metric(fn):
@functools.wraps(fn) # 復制原函數信息 到 wrapper
def wrapper(*args, **kw):
begin=time.time()
res=fn(*args, **kw) # 將fn 函數的返回值 賦給 res
end=time.time()
print("%s executed in %s ms" % (fn.__name__,(end-start)*1000)) # 打印執行時間 ms
return res
return wrapper
2、在函數的執行前後打印標記
def log(func):
def call(*args,**kw):
print('begin call')
out=func(*args,**kw) # 要點在於 將函數的返回值 賦值給一個變量,最後return 變量
print('end call')
return out
return call
Python學習筆記__4.4章 裝飾器(添加額外功能)