装饰器
函数 - > 装饰器
- 在Python中,函数也是对象,可以把函数赋予变量**
def func(message):
print('Got a message:{}'.format(message))
send_message = func
send_message('hello world')
# 输出
Got a message: hello world
函数func赋予了变量send_message,调用send_message,就相当于调用函数func()
- 可以把函数当作参数,传入另一个函数中
def get_message(message):
return 'Got a message:' + message
def root_call(func, message):
print(func(message))
root_call(get_message, 'hello world')
# 输出
Got a message: hello world
把函数get_message以参数的形式,传入了函数root_call()中然后调用它。
- 可以在函数里定义函数,也就是函数的嵌套。
def func(message):
def get_message(message):
print('Got a message: {}'.format(message))
return get_message(message)
func('hello world')
# 输出
Got a message: hello world
在函数func()里定义了新的函数get_message(),调用func()的返回值返回。
- 函数的返回值也可以是函数对象(闭包)
def func_closure():
def get_message(message):
print('Got a message: {}'.format(message))
return get_message
send_message = func_closure()
send_message('hello world')
# 输出
Got a message: hello world
函数func_closure()的返回值是函数对象get_message。
简单的装饰器
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
def greet():
print('hello world')
greet = my_decorator(greet)
greet()
# 输出
wrapper of decorator
hello world
变量greet指向了内部函数wrapper(),而内部函数wrapper()中又调用原函数greet()。
这里的函数my_decorator()就是一个装饰器,它把真正需要执行的函数greet()包裹在其中,并且改变了它的行为,但是原函数greet()不变。
Python中更简单、更优雅的表示
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
@my_decorator
def greet():
print('hello world')
greet()
@为语法糖,@my_decorator就相当于前面的greet=my_decorator(greet)语句,只不过更加简洁。
带有参数的装饰器
如果原函数中,有参数需要传递给装饰器,那么可以在对应的装饰器函数上,加上相应的参数。
def my_decorator(func):
def wrapper(message):
print('wrapper of decorator')
func(message)
return wrapper
@my_decorator
def greet(message):
print(message)
greet('hello world')
# 输出
wrapper of decorator
hello world
通常情况下,会把*args和**kwargs,作为装饰器内部函数的参数。
def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
带有自定义参数的装饰器
装饰器可以接受原函数任意类型和数量的参数,除此之外,还可以接受自己定义的参数。
实例
- 定义一个参数,来表示装饰器内部函数被执行的次数
def repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
return my_decorator
@repeat(4)
def greet(message):
print(message)
greet('hello world')
# 输出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
原函数还是原函数吗?
打印之前例子的元信息
greet.__name__
## 输出
'wrapper'
help(greet)
# 输出
Help on function wrapper in module __main__:
wrapper(*args, **kwargs)
greet()函数被装饰以后,它的元信息变了。不再是以前的greet()函数,而是被wrapper()函数取代了。
为了解决这个问题,通常使用内置的装饰器@functools.wrap,它会帮助保留言函数的元信息(也就是将原函数的元信息,拷贝到对一个的装饰器函数里)
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
@my_decorator
def greet(message):
print(message)
greet.__name__
# 输出
'greet'
类装饰器
类装饰器主要以来于函数__call__()。每调用一个类的示例时,函数__call__()就会被执行一次。
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)
@Count
def example():
print('hello world')
example()
# 输出
num of calls is: 1
hello world
example()
# 输出
num of calls is: 2
hello world
...
装饰器的嵌套
Python支持多个装饰器
@decorator1
@decorator2
@decorator3
def func():
...
执行顺序从里到外
decorator1(decorator2(decorator3(func)))
示例
import functools
def my_decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator1')
func(*args, **kwargs)
return wrapper
def my_decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator2')
func(*args, **kwargs)
return wrapper
@my_decorator1
@my_decorator2
def greet(message):
print(message)
greet('hello world')
# 输出
execute decorator1
execute decorator2
hello world
装饰器用法实例
身份认证
- 登陆时,需要输入用户密码,然后点击确认,如果认证通过,就可以顺利登陆;如果不通过,就抛出异常并提示你登录失败。
import functools
def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
if check_user_logged_in(request): # 如果用户处于登录状态
return func(*args, **kwargs) # 执行函数post_comment()
else:
raise Exception('Authentication failed')
return wrapper
@authenticate
def post_comment(request, ...)
...
日记记录
找到某些函数的耗时过长,导致整个系统的latency(延迟)增加。
import time
import functools
def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
return res
return wrapper
@log_execution_time
def calculate_similarity(items):
...