1. 装饰器的概念
Python中的装饰器是一种函数(或类)包装器,通常用来修改被包装函数的功能。具体而言,装饰器函数接受一个函数对象作为输入,然后返回一个新的函数对象,用于包装原始函数。
下面是一个简单的装饰器示例:
def my_decorator(func):
def wrapper():
print("Before function call")
func()
print("After function call")
return wrapper
@my_decorator
def say_hello():
print("Hello World")
say_hello()
在上面的代码中,my_decorator是一个装饰器函数,它接受一个函数作为输入,然后返回一个闭包函数wrapper。该闭包函数在调用被装饰函数之前和之后打印一些文本。装饰器使用@my_decorator语法应用到函数say_hello()上。这意味着调用say_hello()实际上是调用my_decorator(say_hello)(),其中返回的新函数代表了原始函数的增强版。
运行上面的代码,输出如下:
Before function call
Hello World
After function call
2. 装饰器的实际作用
2.1 缓存
装饰器可以用来实现缓存,即避免重复计算或数据库查询等开销昂贵的操作。下面的装饰器演示了如何记录函数的返回值并使用缓存提高函数性能:
def memoize(func):
memo = {}
def wrapper(*args):
if args in memo:
return memo[args]
result = func(*args)
memo[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(40))
在上面的示例中,memoize是一个装饰器函数,它创建了一个字典存储原始函数的返回值,以及每个函数参数值的哈希值。使用@memoize语法应用到函数fibonacci()上,这意味着函数调用将被缓存,避免了重复计算。通过加入缓存逻辑,函数的计算时间从几分钟缩短到几秒之内。
2.2 记录日志
装饰器可以用于记录函数调用信息以及其他与应用程序相关的信息,例如时间戳,线程ID等等。下面的装饰器演示了如何使用日志模块实现消息日志记录:
import logging
def log_message(message):
def wrapper(func):
logging.basicConfig(level=logging.DEBUG)
def inner(*args, **kwargs):
logging.debug(f"{message} : {args}")
return func(*args, **kwargs)
return inner
return wrapper
@log_message("Starting function")
def my_function(x, y):
return x+y
print(my_function(1, 2))
在上面的示例中,log_message是一个装饰器函数,它创建了一个嵌套的函数wrapper,该函数记录函数的参数和执行时间,并将它们写入日志文件。使用@log_message("Starting function")语法应用到函数my_function()上,这意味着将打印Starting function : (1, 2),然后返回函数结果3。日志记录非常有用,因为它可以提供有关程序执行的非常详细的信息,同时也有助于排除问题。
2.3 计时器
另一种常见的应用程序是使用装饰器来创建计时器,即记录函数执行所需的时间。下面的装饰器演示了如何使用time模块实现函数调用的时间测量:
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
run_time = end_time - start_time
print(f"{func.__name__} executed in {run_time:.4f} seconds")
return result
return wrapper
@timer
def my_function():
time.sleep(1)
print("Function executed")
return
my_function()
在上面的示例中,timer是一个装饰器函数,它创建了一个闭包函数wrapper,该函数测量函数执行的时间,并将其打印出来。使用@timer语法应用到函数my_function()上,这意味着函数执行将被计时,并且结果将被打印。运行上面的代码将打印出my_function executed in 1.0003 seconds。
2.4 权限检查
装饰器可以用于实现对使用者的权限检查,以便限制某些用户对特定函数或应用程序资源的访问。下面的装饰器演示了在调用函数之前检查用户权限:
def login_required(func):
def wrapper(user):
if user.is_authenticated:
return func(user)
else:
return "Please log in to access this resource."
return wrapper
@login_required
def secret_function(user):
return "You've accessed a secret resource."
class User:
def __init__(self, name, email, password, is_authenticated=False):
self.name = name
self.email = email
self.password = password
self.is_authenticated = is_authenticated
user1 = User("Alice", "alice@example.com", "123456", True)
user2 = User("Bob", "bob@example.com", "abcdef", False)
print(secret_function(user1))
print(secret_function(user2))
在上面的示例中,login_required是一个装饰器函数,如果用户已通过身份验证,则调用原始函数,否则返回请求用户进行身份验证的消息。使用@login_required语法应用到函数secret_function()上,这意味着调用函数之前将进行身份验证。运行上面的代码将输出You've accessed a secret resource.(对于用户Alice),或者Please log in to access this resource.(对于用户Bob)。
2.5 重试机制
在某些情况下,函数可能会由于网络超时等问题导致失败。在这种情况下,可以使用装饰器来实现自动重试机制,以便在失败的情况下重新尝试函数调用。下面的装饰器演示了如何实现一个具有自动重试的函数调用:
import time
def retry(func):
def wrapper(*args, **kwargs):
MAX_RETRIES = 3
retry_count = 0
while True:
try:
result = func(*args, **kwargs)
return result
except Exception as e:
retry_count += 1
if retry_count >= MAX_RETRIES:
raise e
print(f"Error occurred, retrying in 2 seconds... ({retry_count}/{MAX_RETRIES})")
time.sleep(2)
return wrapper
@retry
def my_function():
if time.perf_counter() % 2 == 0:
return "Success"
else:
raise ValueError("Something went wrong")
print(my_function())
在上面的示例中,retry是一个装饰器函数,它使用一个while循环重试函数调用(最多3次),直到函数返回一个非异常结果。如果达到重试的最大次数还未成功,则函数将重新引发原始异常。 使用@retry语法应用到函数my_function()上,这意味着函数失败后将被重试,以尝试获取非异常结果。运行上面的代码将打印Error occurred, retrying in 2 seconds... (1/3),然后再次调用函数,最终返回Success。
3. 总结
本文介绍了Python装饰器的基本概念以及几种常见的实际用途,包括缓存、日志记录、计时器、权限检查和重试机制。装饰器是Python编程中非常有用和强大的工具,可用于增强函数或类的功能,提高代码性能,同时也提高了代码的可读性和可维护性。