Python中的装饰器及其用法
2020年12月25日 - 由Bo 0 评论 1213 阅读
python程序中经常能看到在函数上有类似@decorator的写法,这就是一个装饰器,它可以帮我们把代码写得更简洁更精炼。
@property和@classmethod你一定非常熟悉;如果你用过pytest的话,那么@pytest.mark.hookwrapper和@pytest.hookimpl(hookwrapper=True)你一定不会陌生。
装饰器可以做为一个统一的增强或者修改函数的方式,而不改变原函数的功能和参数。
最简单的装饰器
比如现在有一个输出当前时间的函数:
import datetime
def record_time_now():
print(datetime.datetime.now())
record_time_now()
假设我们需要在输出时间之前输出当前用户名,那么在print(datetime.datetime.now())上一行加一句即可。
但是如果现在有许多的函数需要修改,并且根据不同的条件输出不同的信息,那么就得在每一个函数里做修改,这样很增加很多冗余的代码。
于是我们可以用装饰器写明需要在函数之外做什么事,再指定该装饰器到函数即可。
import datetime
def show_log(func):
def inner():
print("---show log here---")
func()
return inner
@show_log
def record_time_now():
print(datetime.datetime.now())
record_time_now()
这里在输出时间之前先输出一句提示。
当函数需求参数时,装饰器应该这么写
往往函数是需要传递参数的,对于这种,装饰器也需要指明,但参数可以用args和kwargs来代替,不用一个个显式写明。
比如如下代码在加法和除法前后输出了当前时间:
import datetime
def show_time(func):
def wrapper(*args, **kwargs):
print(datetime.datetime.now())
f = func(*args, **kwargs)
print(datetime.datetime.now())
return f
return wrapper
@show_time
def add(a, b):
return a + b
@show_time
def divide(a, b):
return a / b
print(add(3, 5))
print(divide(99, 11))
装饰器也可以有参数
在我们给函数指定装饰器时,也可以传递参数给装饰器,以达成不同条件下的适应。
比如:
def logging(level):
def wrapper(func):
def inner(*args, **kwargs):
print(f"{level}: ---now '{func.__name__}' get a {level} log---")
return func(*args, **kwargs)
return inner
return wrapper
@logging(level="Debug")
def add(a, b):
return a + b
@logging(level="Info")
def divide(a, b):
return a / b
print(add(3, 5))
print(divide(99, 11))
## output:
# Debug: ---now 'add' get a Debug log---
# 8
# Info: ---now 'divide' get a Info log---
# 9.0
多个装饰器的执行顺序
多个装饰器装饰的顺序是从里到外(就近),而调用的顺序是从外到里(就远)。
比如:
def decorator1(func):
print("outer decorator1 top")
def wrapper():
print("innter decorator1 top")
func()
print("inner decorator1 bottom")
print("outer decorator1 bottom")
return wrapper
@decorator1
def temp():
print("this is temp function")
temp()
## output:
# outer decorator1 top
# outer decorator1 bottom
# innter decorator1 top
# this is temp function
# inner decorator1 bottom
当只有一个装饰器时,是先执行wrapper之外的语句,然后再依次执行wrapper里的语句。
此时加上一个装饰器:
def decorator1(func):
print("outer decorator1 top")
def wrapper():
print("innter decorator1 top")
func()
print("inner decorator1 bottom")
print("outer decorator1 bottom")
return wrapper
def decorator2(func):
print("outer decorator2 top")
def wrapper():
print("innter decorator2 top")
func()
print("inner decorator2 bottom")
print("outer decorator2 bottom")
return wrapper
@decorator2
@decorator1
def temp():
print("this is temp function")
temp()
## output:
# outer decorator1 top
# outer decorator1 bottom
# outer decorator2 top
# outer decorator2 bottom
# innter decorator2 top
# innter decorator1 top
# this is temp function
# inner decorator1 bottom
# inner decorator2 bottom
此时先执行隔temp函数最近的decorator1的wrapper外,再执行decorator2的wrapper外;接着再执行decorator2的wrapper内,再是decorator1的wrapper内。可以注意output的最后五行,可以看出decorator2是把temp加上decorator1一起做了装饰。