在理解裝飾器的概念前,我們首先看一下function和inner function在python中的含義。 function與inner function function:在python中,function就像其他對象一樣,可以當做參數或返回值。 inner function:一個函數A(inne ...
在理解裝飾器的概念前,我們首先看一下function和inner function在python中的含義。
function與inner function
-
function:在python中,function就像其他對象一樣,可以當做參數或返回值。
-
inner function:一個函數A(inner function)定義在另一個函數B中。函數A就叫做inner function。
# 函數用做參數
def hello():
print("hello")
def welcome():
print("Welcome")
def say_something(func):
func()
# 函數add在函數cal中定義,並且用做返回值
def cal():
def add(a,b):
print(a+b)
return add
註意:此時add不能直接被外界調用,因此此時內部函數還未定義。但可以通過cal將add的引用返回,用於在將來調用。
decorator
decorator:封裝了一個函數,並且改變了其行為
裝飾器本質上,是將要被裝飾的函數作為參數,並且返回一個包裹其的、內部定義的函數。如下所示:my_decorator返回了wrapper的引用,用於將在將來執行。
def hello():
print("hello")
def my_decorator(func):
def wrapper():
print("before func work")
func()
print("after func work")
return wrapper
hello_d = my_decorator(hello)
hello_d()
"""
before func work
hello
after func work
"""
hello_d便是經過my_decorator裝飾的函數。我們明顯發現,過程有些複雜,為了簡化步驟,python提供了@decorator
的語法糖。
下麵,我們使用@decorator
達到同樣的目的。
@my_decorator
def hello():
print("hello")
def my_decorator(func):
def wrapper():
print("before func work")
func()
print("after func work")
return wrapper
hello()#與hello = my_decorator(hello)作用相同
"""
before func work
hello
after func work
"""
在裝飾器中,wrapper的命名可自定義。
@functools.wrap(func)
觀察上面的例子,由於hello被my_decoratorx裝飾過,此時在python shell查看hello的__name__,發現
這對我們來說,不是有用的信息,為了“正確”顯示,使用@functools.wrap(func)
import functools
@my_decorator
def hello():
print("hello")
def my_decorator(func):
@functools.wrap(func)
def wrapper():
print("before func work")
func()
print("after func work")
return wrapper
然後便可以達到和裝飾器裝飾前同樣的效果:
帶參數的decorator以及幾個例子
# do twice
def do_twice(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
func(*args,**kwargs)
func(*args,**kwargs)
return wrapper
# timer
def timer(func):
@functools.wraps(func)
def wrapper_timer(*args, **kwargs):
start_time = time.perf_counter()
value = func(*args, **kwargs)
end_time = time.perf_counter()
run_time = end_time - start_time
print(f"Finished {func.__name__} in {run_time:.4f} seconds")
return value
return wrapper_timer
@timer
def waste_time(num):
for _ in range(num):
sum([i **2 for i in range(10000)])
waste_time(1000)
# flask check user login
def login_required(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if g.user is None:
return redirect(url_for('login',next=request.url))
return func(*args, **kwargs)
return wrapper
@app.route('/secret')
@login_required
def secret():
...
總結
decorator用於“包裹”一個函數,改變其行為。
在裝飾器的wrapper中並不一定要執行該函數,也可以保留函數的引用,用於插件的註冊。