什么是装饰器?
装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的基本概念
想象一下,你有一个函数,你想在不改变这个函数本身的前提下,给它添加一些额外的功能,比如日志记录、性能测试、权限校验等。装饰器就是为了解决这类问题而生的。
# 最简单的装饰器示例
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
# 调用函数
say_hello()
输出结果:
函数执行前
Hello!
函数执行后
装饰器的工作原理
1. 函数作为一等公民
在Python中,函数是一等公民,这意味着函数可以:
- 被赋值给变量
- 作为参数传递给其他函数
- 作为其他函数的返回值
def greet():
return "Hello!"
# 函数赋值给变量
say_hello = greet
print(say_hello()) # 输出: Hello!
# 函数作为参数
def call_function(func):
return func()
print(call_function(greet)) # 输出: Hello!
2. 闭包的概念
装饰器依赖于闭包的概念。闭包是指一个函数可以访问其外部作用域的变量,即使外部函数已经执行完毕。
def outer_function(msg):
message = msg
def inner_function():
print(message)
return inner_function
my_func = outer_function("Hello, World!")
my_func() # 输出: Hello, World!
基础装饰器实现
1. 不带参数的函数装饰器
import time
def timer_decorator(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:.4f}秒")
return result
return wrapper
@timer_decorator
def calculate_sum(n):
total = 0
for i in range(n):
total += i
return total
# 测试
result = calculate_sum(1000000)
print(f"计算结果: {result}")
2. 带参数的函数装饰器
如果需要装饰器本身接受参数,需要多一层包装:
def repeat_decorator(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat_decorator(3)
def greet(name):
print(f"Hello, {name}!")
return name
greet("Alice")
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
高级装饰器技术
1. 使用functools.wraps
当我们使用装饰器时,原函数的元信息(如函数名、文档字符串等)会被包装函数覆盖。为了解决这个问题,可以使用functools.wraps:
import functools
def debug_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
print(f"参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper
@debug_decorator
def add(a, b):
"""计算两个数的和"""
return a + b
print(add.__name__) # 输出: add (而不是wrapper)
print(add.__doc__) # 输出: 计算两个数的和
2. 类装饰器
除了函数装饰器,Python还支持类装饰器:
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"函数被调用了 {self.num_calls} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
say_hello()
输出:
函数被调用了 1 次
Hello!
函数被调用了 2 次
Hello!
函数被调用了 3 次
Hello!
3. 多个装饰器的组合
多个装饰器可以堆叠使用,执行顺序是从下到上:
def bold_decorator(func):
def wrapper():
return f"<b>{func()}</b>"
return wrapper
def italic_decorator(func):
def wrapper():
return f"<i>{func()}</i>"
return wrapper
@bold_decorator
@italic_decorator
def hello():
return "Hello!"
print(hello()) # 输出: <b><i>Hello!</i></b>
实际应用场景
1. 日志记录
import logging
from functools import wraps
def log_execution(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(func.__name__)
logger.info(f"开始执行 {func.__name__}")
try:
result = func(*args, **kwargs)
logger.info(f"{func.__name__} 执行成功")
return result
except Exception as e:
logger.error(f"{func.__name__} 执行失败: {str(e)}")
raise
return wrapper
@log_execution
def divide(a, b):
return a / b
# 测试
try:
result = divide(10, 2)
print(f"结果: {result}")
divide(10, 0)
except:
pass
2. 权限验证
def require_admin(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('role') != 'admin':
raise PermissionError("需要管理员权限")
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_user(user, target_user):
print(f"用户 {user['name']} 删除了用户 {target_user}")
# 测试
admin_user = {'name': 'Alice', 'role': 'admin'}
regular_user = {'name': 'Bob', 'role': 'user'}
delete_user(admin_user, "Charlie") # 成功
try:
delete_user(regular_user, "Charlie") # 失败
except PermissionError as e:
print(e)
3. 缓存/记忆化
import time
from functools import wraps
def cache(seconds):
def decorator(func):
cache_dict = {}
@wraps(func)
def wrapper(*args):
current_time = time.time()
# 清理过期的缓存
expired_keys = [k for k, v in cache_dict.items() if current_time - v['time'] > seconds]
for k in expired_keys:
del cache_dict[k]
# 检查是否有缓存
if args in cache_dict:
print(f"使用缓存结果: {args}")
return cache_dict[args]['result']
# 执行函数并缓存结果
result = func(*args)
cache_dict[args] = {'result': result, 'time': current_time}
print(f"计算新结果: {args}")
return result
return wrapper
return decorator
@cache(5) # 缓存5秒
def expensive_calculation(n):
time.sleep(1) # 模拟耗时计算
return n * n
# 测试
print(expensive_calculation(5))
print(expensive_calculation(5))
time.sleep(6) # 等待缓存过期
print(expensive_calculation(5))
4. 性能监控
import time
from functools import wraps
import functools
def performance_monitor(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 执行耗时: {end - start:.6f} 秒")
return result
return wrapper
@performance_monitor
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试
print(f"Fibonacci(30) = {fibonacci(30)}")
装饰器的高级用法
1. 带参数的类装饰器
class ValidateInput:
def __init__(self, min_value=None, max_value=None):
self.min_value = min_value
self.max_value = max_value
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if self.min_value is not None and arg < self.min_value:
raise ValueError(f"参数 {arg} 小于最小值 {self.min_value}")
if self.max_value is not None and arg > self.max_value:
raise ValueError(f"参数 {arg} 大于最大值 {self.max_value}")
return func(*args, **kwargs)
return wrapper
@ValidateInput(min_value=0, max_value=100)
def set_age(age):
print(f"设置年龄为: {age}")
set_age(25) # 正常
try:
set_age(150) # 异常
except ValueError as e:
print(e)
2. 装饰器保留原函数调用方式
class CallableDecorator:
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
print(f"装饰器处理: {self.func.__name__}")
return self.func(*args, **kwargs)
def __get__(self, instance, owner):
# 支持类方法
if instance is None:
return self
return functools.partial(self.__call__, instance)
@CallableDecorator
def my_function(x, y):
return x + y
# 作为普通函数调用
print(my_function(3, 4))
# 作为方法调用
class MyClass:
@CallableDecorator
def method(self, x):
return x * 2
obj = MyClass()
print(obj.method(5))
装饰器的调试技巧
1. 使用inspect模块检查装饰器
import inspect
def debug_decorator(func):
def wrapper(*args, **kwargs):
print(f"函数签名: {inspect.signature(func)}")
print(f"参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper
@debug_decorator
def example(a, b, c=10):
return a + b + c
example(1, 2, c=3)
2. 装饰器的单元测试
import unittest
def test_decorator():
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return wrapper
@my_decorator
def add(a, b):
return a + b
assert add(1, 2) == 6 # (1+2)*2 = 6
print("测试通过!")
test_decorator()
装饰器的性能考虑
1. 装饰器的性能开销
import time
def no_decorator():
total = 0
for i in range(1000000):
total += i
return total
def with_decorator():
def dummy_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@dummy_decorator
def inner():
total = 0
for i in range(1000000):
total += i
return total
return inner()
# 性能比较
start = time.time()
no_decorator()
print(f"无装饰器: {time.time() - start:.4f}秒")
start = time.time()
with_decorator()
print(f"有装饰器: {time.time() - start:.4f}秒")
2. 优化装饰器性能
# 使用__slots__减少内存占用
class OptimizedDecorator:
__slots__ = ('func',)
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
装饰器的最佳实践
1. 保持装饰器简单
每个装饰器应该只做一件事。如果需要多个功能,使用多个装饰器。
# 好的做法
@log_execution
@validate_input
@performance_monitor
def complex_function():
pass
# 不好的做法
@all_in_one_decorator # 一个装饰器做太多事情
def complex_function():
pass
2. 文档化装饰器
def rate_limit(max_calls, period):
"""
限制函数在指定时间内的调用次数
Args:
max_calls (int): 最大调用次数
period (int): 时间周期(秒)
Returns:
function: 装饰器函数
Example:
@rate_limit(5, 60) # 每分钟最多5次调用
def api_call():
pass
"""
def decorator(func):
call_history = []
@functools.wraps(func)
def wrapper(*args, **kwargs):
current_time = time.time()
# 清理过期记录
call_history[:] = [t for t in call_history if current_time - t < period]
if len(call_history) >= max_calls:
raise Exception("调用频率过高")
call_history.append(current_time)
return func(*args, **kwargs)
return wrapper
return decorator
总结
装饰器是Python中一个非常强大且灵活的特性,它允许我们以声明式的方式为函数添加行为。通过本文的详细讲解和丰富的示例,你应该已经掌握了:
- 基础概念:理解装饰器的工作原理和闭包的概念
- 基本语法:学会如何创建和使用简单的装饰器
- 高级技巧:掌握带参数的装饰器、类装饰器、多个装饰器组合等
- 实际应用:了解装饰器在日志记录、权限验证、缓存、性能监控等场景的应用
- 最佳实践:知道如何编写可维护、高性能的装饰器
装饰器是Python编程中不可或缺的工具,熟练掌握它们将大大提升你的代码质量和开发效率。记住,好的装饰器应该简单、专注、文档完善,并且考虑到性能影响。
在实际项目中,装饰器可以帮助你实现关注点分离,使业务逻辑更加清晰,代码更加模块化和可重用。继续练习和探索,你会发现更多装饰器的妙用!
