函数回顾
函数可以作为变量
def func(message):
print('Got a message: {}'.format(message))
send_message = func # 作为值赋值其他的变量
send_message('hello world')
# 输出
Got a message: hello world
函数作为参数传递
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
函数嵌套定义
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
函数可以作为函数的返回值【闭包】
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
装饰器
简单装饰器
# 装饰器
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func() # 被装饰的函数
return wrapper
# 实例函数
def greet():
print('hello world')
# 装饰greet
greet = my_decorator(greet) # 返回装饰后的结果
greet()
# 输出
wrapper of decorator
hello world
另一种语法糖的写法就是注解
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
@my_decorator # 其他的函数也可以使用这个注解
def greet():
print('hello world')
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
如果有函数也需要这个注解,但是函数有多个参数,怎么办
def my_decorator(func):
def wrapper(*args, **kwargs): # 这样就是传递任意数量和任意类型的参数了
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
*args, **kwargs
这样就是传递任意数量和任意类型的参数了
带有自定义参数的装饰器
因为注解也是一个函数,所以自己也可以有参数
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函数了
增加 @functools.wraps(func)
注解,保留原函数的元信息
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'
类也可以作为装饰器
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
...
装饰器的嵌套
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, ...)
...
执行时间计算
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):
...
输入合理性检查
import functools
def validation_check(input):
@functools.wraps(func)
def wrapper(*args, **kwargs):
... # 检查输入是否合法
@validation_check
def neural_network_training(param1, param2, ...):
...
LRU cache,在 Python 中的表示形式是@lru_cache。@lru_cache会缓存进程中的函数参数和结果,当缓存满了以后,会删除 least recenly used 的数据。
缓存
@lru_cache
def check(param1, param2, ...) # 检查用户设备类型,版本号等等
...
总结
通过基础和实例,了解了装饰器 主要是在不修改原函数的基础上,修改了函数的行为,扩展性好
相关问答
- 我感觉python的装饰器的应用场景有点像AOP的应用场景,把一些常用的业务逻辑分离,提高程序可重用性,降低耦合度,提高开发效率
- 请教下,为什么count那儿是单例模式吗?为什么二次执行会加1?
作者回答:因为num_calls这个变量是类变量,不是具体的实例变量,二次执行相当于调用了函数__call__两次,因此变量num_calls会变为2
但是使用self赋值的变量应该就是实例变量 ??