1. 什么是装饰器?
@装饰器
装饰器定义
装饰器是一种特殊的函数,它可以用来修改其他函数的行为。装饰器通过接受一个函数作为参数,然后返回一个新的函数来实现修改行为的功能。
装饰器在 Python 中使用 @
符号来定义。例如:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("开始执行函数:", func.__name__)
result = func(*args, **kwargs)
print("函数执行结束:", func.__name__)
return result
return wrapper
@my_decorator
def my_function():
print("这是我的函数")
my_function()
在这个例子中,my_decorator
是一个装饰器,它接受 my_function
函数作为参数,然后返回一个新的函数 wrapper
。当 my_function
被调用时,实际上是 wrapper
函数被执行。wrapper
函数在执行 my_function
之前和之后打印了一些信息。
如何使用装饰器
使用装饰器非常简单,只需要在要装饰的函数前面添加 @
符号和装饰器函数的名称即可。例如:
@my_decorator
def my_function():
print("这是我的函数")
my_function()
这等价于:
def my_function():
print("这是我的函数")
my_function = my_decorator(my_function)
my_function()
输出:
开始执行函数: my_function
这是我的函数
函数执行结束: my_function
装饰器的优点
装饰器有以下几个优点:
- 代码可重用性: 装饰器可以将一些通用的功能封装起来,方便重复使用。
- 代码可读性: 装饰器可以使代码更加简洁易读。
- 代码可维护性: 装饰器可以使代码更容易维护和修改。
装饰器的应用
装饰器可以用于各种场景,例如:
- 计时: 记录函数执行时间。
- 缓存: 缓存函数结果。
- 权限控制: 限制函数的访问权限。
- 日志记录: 记录函数执行信息。
- 异常处理: 处理函数执行过程中出现的异常。
2. 装饰器应用举例
2.1 计时
计时装饰器,可以记录函数执行时间,方便调试和性能分析。计时装饰器会增加函数执行时间,因此不建议在生产环境中使用。
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 执行时间:{end_time - start_time:.2f} 秒")
return result
return wrapper
@timer
def my_function():
time.sleep(1)
my_function()
输出:
函数 my_function 执行时间:1.00 秒
2.2 缓存
可以将函数的结果缓存起来,避免重复计算,提高程序效率
import functools
def cache(func):
cache_dict = {}
# 使用 functools.wraps 装饰器来保留 func 的元信息,例如函数名、文档字符串等
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
if key not in cache_dict:
cache_dict[key] = func(*args, **kwargs)
return cache_dict[key]
return wrapper
@cache
def my_function(x):
return x * x
print(my_function(3))
print(my_function(3))
#第一次调用 my_function(3) 时,由于缓存中没有结果,所以会执行 my_function 函数并将其结果存储在缓存中。
#第二次调用 my_function(3) 时,由于缓存中已经存在结果,所以直接从缓存中返回结果,不会再次执行 my_function 函数。
输出:
9
9
2.3 权限控制
可以限制函数的访问权限,确保只有具有相应权限的用户才能执行该函数
def admin_required(func):
def wrapper(*args, **kwargs):
#is_admin() 函数需要根据实际情况进行定义,以判断当前用户是否具有管理员权限
if not is_admin():
raise PermissionError("您没有权限执行此操作")
return func(*args, **kwargs)
return wrapper
@admin_required
def delete_user(user_id):
# 删除用户
pass
delete_user(123)
输出:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in wrapper
PermissionError: 您没有权限执行此操作
2.4 日志记录
使用 log 装饰器包装 my_function 函数,使其在执行前后记录日志消息
import logging
def log(func):
def wrapper(*args, **kwargs):
logging.info(f"开始执行函数 {func.__name__}")
result = func(*args, **kwargs)
logging.info(f"函数 {func.__name__} 执行结束")
return result
return wrapper
@log
def my_function():
# 执行一些操作
pass
my_function()
输出:
INFO:root:开始执行函数 my_function
INFO:root:函数 my_function 执行结束
2.5 异常处理
使用 handle_exceptions 装饰器包装 my_function 函数,使其在执行过程中捕获并处理异常
def handle_exceptions(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"函数 {func.__name__} 出现异常:{e}")
return wrapper
@handle_exceptions
def my_function():
1 / 0
my_function()
输出:
函数 my_function 出现异常:division by zero
3. functools.wraps()使用介绍
Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和函数属性
。
不加wraps
def my_decorator(func):
def wrapper(*args, **kwargs):
'''this is decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""this is Docstring"""
print('Called example function')
print('name: {}'.format(example.__name__))
print('docstring: {}'.format(example.__doc__))
输出:
name: wrapper
docstring: this is decorator
加上wraps
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
'''decorator'''
print('Calling decorated function...')
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Docstring"""
print('Called example function')
print('name: {}'.format(example.__name__))
print('docstring: {}'.format(example.__doc__))
输出:
name: example
docstring: this is Docstring
参考链接:
https://blog.csdn.net/weixin_43750377/article/details/115112517