Python装饰器的常见用途是什么?
在Python中,装饰器是一种函数,它可以修改其他函数的行为。装饰器可以被认为是函数的 “包装器”,它给函数新增了额外的功能。装饰器在Python中是非常有用的,特别是在函数式编程和面向切面编程中。
阅读更多:Python 教程
为什么使用装饰器?
在编写函数时,我们经常会遇到以下情况:
- 需要添加一些通用的功能,但是不想修改函数本身的代码;
- 想要在函数被调用或者返回前后执行一些操作。
这些问题可以通过使用装饰器来解决,让我们看看装饰器的常见用途。
1. 函数缓存
函数缓存可以帮助我们提高代码性能,减少重复计算。下面是一个非常简单的示例,演示了如何使用装饰器实现函数缓存:
import functools
@functools.lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
这个缓存装饰器使用Python内置的 functools.lru_cache 实现。它用于保存最近使用的函数结果,在缓存中查找参数的值以获取结果,而不是执行该函数。在上面的示例中,当调用 fibonacci(50) 后,结果会被缓存下来,以便下次调用时快速返回。
2. 计时器
有时候我们需要了解程序运行时间以便调试和优化。下面的装饰器可以帮助我们计时一个函数的执行时间:
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f'{func.__name__} took {end_time - start_time:.8f}s')
return result
return wrapper
这个装饰器在函数开始和结束时记录时间,并输出函数执行的总时间。我们可以在需要使用计时器的函数上方加上 @timer 来装饰函数。
3. 日志
日志是我们记录程序状态的重要方法。下面的装饰器可以帮助我们记录函数被调用的次数、参数和返回值:
import functools
def logger(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logger_string = f'{func.__name__} called with args={args} kwargs={kwargs}'
result = func(*args, **kwargs)
logger_string += f' returned {result}'
print(logger_string)
return result
return wrapper
这个装饰器记录被装饰的函数被调用的次数、输入的参数以及函数的返回值。我们可以在需要使用日志的函数上方加上 @logger 来装饰函数。
4. 权限检查
当我们编写一个需要身份验证的Web应用程序时,经常需要检查用户是否具有足够的权限来执行某些操作。下面的装饰器演示了如何实现授权检查:
def authenticate(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.is_authenticated:
return func(user, *args, **kwargs)
else:
raise ValueError("User is not authenticated")
return wrapper
@authenticate
def delete_user(user, user_id):
# delete user with user_id
pass
这个装饰器检查用户是否已经通过身份验证,如果是,则允许函数执行。否则,它会引发一个值错误并防止该函数执行。
5. 缓存结果
类似于函数缓存,结果缓存可以帮助我们减少重复计算,但是与函数缓存不同,它可以缓存一个函数的结果而不是参数。下面的装饰器演示了如何实现结果缓存:
import functools
def cache_result(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
if args not in cache or kwargs not in cache[args]:
result = func(*args, **kwargs)
if args not in cache:
cache[args] = {}
cache[args][kwargs] = result
return cache[args][kwargs]
return wrapper
这个装饰器在缓存中存储一个函数的结果。如果输入的参数已经被缓存了,那么直接返回结果,否则执行函数并将结果存储在缓存中。我们可以在需要使用结果缓存的函数上方加上 @cache_result 来装饰函数。
6. 重试
当我们执行一些不稳定的操作时,例如网络请求或者文件读写操作,有时我们需要对操作失败进行重试。下面的装饰器演示了如何实现函数重试:
import time
import functools
def retry(attempts, delay=1):
def decorator_retry(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, attempts+1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Retry attempt {attempt}/{attempts} failed with error: {e}")
time.sleep(delay)
raise Exception(f"All {attempts} retry attempts failed")
return wrapper
return decorator_retry
这个装饰器将函数执行的最大重试次数作为输入,并将一个 delay 参数作为可选参数。如果函数执行失败,则等待指定的延迟时间后进行重试,直到达到指定的最大尝试次数。我们可以在需要使用重试的函数上方加上 @retry(attempts=3, delay=2) 来装饰函数。
结论
以上是装饰器在Python中的常见用途。使用装饰器可以帮助我们提高代码的可读性和灵活性,减少重复的代码和调试时间。我们可以根据不同的需要编写自己的装饰器或者使用现有的通用装饰器来实现我们的需求。
极客笔记