Python装饰器 - 探究Python中的装饰器应用及其实现方法
Python中的装饰器是一种非常实用的语言特性,可以方便地实现代码的复用、增强和扩展。它本质上是一种函数,可以接受原函数作为参数,对其进行包装并返回包装后的函数。这种包装过程可以在不改变原函数功能的情况下,实现一些额外的操作,比如给函数添加新的功能、记录日志、缓存结果等等。
本文将探究Python中的装饰器应用及其实现方法。
一、装饰器使用场景
1. 记录函数执行时间
装饰器可以方便地记录函数执行时间,比如:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} execution time: {end - start}s")
return result
return wrapper
@timeit
def foo():
time.sleep(2)
foo() # 输出: foo execution time: 2.0010483264923096s
2. 缓存函数结果
装饰器可以用来实现缓存函数结果,避免重复计算,比如:
def cache(func):
cached_results = {}
def wrapper(*args):
if args in cached_results:
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fib(n):
if n <= 2:
return 1
return fib(n-1) + fib(n-2)
print(fib(30)) # 输出: 832040
3. 权限验证
装饰器可以用来验证用户权限,比如:
def admin_only(func):
def wrapper(user, *args, **kwargs):
if user != "admin":
raise Exception("Only admin can access this resource")
return func(*args, **kwargs)
return wrapper
@admin_only
def sensitive_info():
return "top secret"
print(sensitive_info("john")) # 输出: Exception: Only admin can access this resource
二、实现装饰器的方法
1. 嵌套函数
装饰器的本质是一个函数,它接受一个函数作为参数,并返回一个函数。一种实现方式是使用嵌套函数,在装饰器内部定义一个包装函数,并将原函数作为参数传入。
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before the function is called.")
result = func(*args, **kwargs)
print("After the function is called.")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"Hello {name}!")
say_hello("Tom") # 输出: Before the function is called. Hello Tom! After the function is called.
2. 类装饰器
装饰器也可以用类来实现。类装饰器定义一个callable类,并实现__init__和__call__方法。
class Decorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Before the function is called.")
result = self.func(*args, **kwargs)
print("After the function is called.")
return result
@Decorator
def say_hello(name):
print(f"Hello {name}!")
say_hello("Tom") # 输出: Before the function is called. Hello Tom! After the function is called.
3. functools库
Python标准库提供了一个functools模块,其中有一个wraps函数,它可以用来在类装饰器中保留原函数的元信息。例如,在保留被装饰函数的docstring和函数名时,可以写成如下形式:
from functools import wraps
class Decorator:
def __init__(self, func):
wraps(func)(self) # 保存原函数的docstring和函数名
self.func = func
def __call__(self, *args, **kwargs):
print("Before the function is called.")
result = self.func(*args, **kwargs)
print("After the function is called.")
return result
@Decorator
def say_hello(name):
"""Prints a greeting message."""
print(f"Hello {name}!")
print(say_hello.__name__) # 输出: 'say_hello'
print(say_hello.__doc__) # 输出: 'Prints a greeting message.'
四、装饰器的应用场景
装饰器的应用非常广泛,下面列出一些典型的应用场景:
1. 计时器
可以用装饰器实现一个简单的计时器,比如:
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Elapsed time: {end_time - start_time:.4f}s")
return result
return wrapper
@timer
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(35) # 输出: Elapsed time: 2.8830s
2. 缓存
可以用装饰器实现一个简单的缓存,避免重复计算,比如:
def cache(func):
cached_results = {}
def wrapper(*args, **kwargs):
key = (args, tuple(kwargs.items()))
if key in cached_results:
print(f"Returning cached result for {args}, {kwargs}")
return cached_results[key]
print(f"Computing result for {args}, {kwargs}")
result = func(*args, **kwargs)
cached_results[key] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(35) # 输出: Computing result for (35,), {}
# Computing result for (34,), {}
# Computing result for (33,), {}
# ...
# Returning cached result for ((2,), {}), {}
# Returning cached result for ((3,), {}), {}
# Returning cached result for ((4,), {}), {}
# Elapsed time: 2.7338s
3. 打印日志
可以用装饰器实现一个简单的日志器,记录函数的调用情况,比如:
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with arguments: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"Result is {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(2, 3) # 输出: Calling add with arguments: (2, 3), {}
# Result is 5
4. 验证权限
可以用装饰器实现一个简单的权限验证器,判断用户是否有访问资源的权限,比如:
def authorized_only(roles):
def decorator(func):
def wrapper(user, *args, **kwargs):
if user["role"] not in roles:
raise Exception(f"Unauthorized access to {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@authorized_only(["admin", "manager"])
def sensitive_info():
return "top secret"
print(sensitive_info({"name": "John", "role": "user"})) # 输出: Exception: Unauthorized access to sensitive_info
5. 批量处理数据
可以用装饰器实现一个简单的批量处理器,把多个调用合并成一个批量调用,比如:
`python
def batch_processor(batch_size):
def decorator(func):
def wrapper(data):
n_batches = (len(data) + batch_size - 1) // batch_size
results = []
for i in range(n_batches):
batch_data = data[i*batch_size : (i+1)*batch_size]
batch_result = func(batch_data)
results.extend(batch_result)
return
