什么是Python装饰器?
Python装饰器(Decorator)是一种强大的语法特性,它允许你在不修改原有函数代码的情况下,动态地改变函数的行为。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的核心概念
def my_decorator(func):
def wrapper():
print("在函数调用前执行")
func()
print("在函数调用后执行")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
这段代码的输出是:
在函数调用前执行
Hello!
在函数调用后执行
为什么需要装饰器?
装饰器在实际开发中非常有用,主要体现在以下几个方面:
- 代码复用:避免在多个函数中重复编写相同的逻辑
- 关注点分离:将核心业务逻辑与辅助功能(如日志、性能监控)分离
- 非侵入式修改:不修改原有函数代码就能添加新功能
装饰器的常见使用场景
1. 日志记录
def log_execution(func):
import logging
logging.basicConfig(level=logging.INFO)
def wrapper(*args, **kwargs):
logging.info(f"开始执行函数: {func.__name__}")
result = func(*args, **kwargs)
logging.info(f"函数执行完成: {func.__name__}")
return result
return wrapper
@log_execution
def calculate_sum(a, b):
return a + b
print(calculate_sum(3, 5))
2. 性能计时
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"函数 {func.__name__} 执行耗时: {end - start:.4f}秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完成"
slow_function()
3. 权限验证
def require_login(func):
def wrapper(user, *args, **kwargs):
if not user.get('is_logged_in'):
raise PermissionError("用户未登录")
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
print(f"查看用户 {user['name']} 的个人资料")
# 测试
logged_in_user = {'name': 'Alice', 'is_logged_in': True}
logged_out_user = {'name': 'Bob', 'is_logged_in': False}
view_profile(logged_in_user) # 正常执行
# view_profile(logged_out_user) # 会抛出PermissionError
带参数的装饰器
有时候我们需要让装饰器本身接受参数,这时需要创建三层嵌套函数:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"你好, {name}!")
greet("World")
输出:
你好, World!
你好, World!
你好, World!
类装饰器
除了函数装饰器,Python还支持类装饰器:
class CountCalls:
def __init__(self, func):
self.func = func
self.call_count = 0
def __call__(self, *args, **kwargs):
self.call_count += 1
print(f"函数 {self.func.__name__} 被调用了 {self.call_count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hi():
print("Hi!")
say_hi()
say_hi()
say_hi()
输出:
函数 say_hi 被调用了 1 次
Hi!
函数 say_hi 被调用了 2 次
Hi!
函数 say_hi 被调用了 3 次
Hi!
装饰器的高级用法
1. 保留原函数元信息
使用functools.wraps保留原函数的名称和文档字符串:
from functools import wraps
def debug(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__} 参数: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@debug
def add(a, b):
"""计算两个数的和"""
return a + b
print(add.__name__) # 输出: add
print(add.__doc__) # 输出: 计算两个数的和
2. 多个装饰器叠加使用
def make_bold(func):
@wraps(func)
def wrapper():
return f"<b>{func()}</b>"
return wrapper
def make_italic(func):
@wraps(func)
def wrapper():
return f"<i>{func()}</i>"
return wrapper
@make_bold
@make_italic
def greet():
return "Hello World"
print(greet()) # 输出: <b><i>Hello World</i></b>
3. 基于类的装饰器带参数
class ValidateInput:
def __init__(self, min_val=None, max_val=None):
self.min_val = min_val
self.max_val = max_val
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
for arg in args:
if self.min_val is not None and arg < self.min_val:
raise ValueError(f"参数 {arg} 小于最小值 {self.min_val}")
if self.max_val is not None and arg > self.max_val:
raise ValueError(f"参数 {arg} 大于最大值 {self.max_val}")
return func(*args, **kwargs)
return wrapper
@ValidateInput(min_val=0, max_val=100)
def set_age(age):
print(f"设置年龄为: {age}")
set_age(25) # 正常执行
# set_age(150) # 会抛出ValueError
实际项目中的装饰器应用
Web框架中的路由系统
# 简化的路由装饰器实现
route_map = {}
def route(path):
def decorator(func):
route_map[path] = func
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
@route("/home")
def home():
return "Welcome to Home Page"
@route("/about")
def about():
return "About Us"
# 模拟路由请求
def handle_request(path):
if path in route_map:
return route_map[path]()
return "404 Not Found"
print(handle_request("/home")) # 输出: Welcome to Home Page
print(handle_request("/about")) # 输出: About Us
缓存装饰器
from functools import lru_cache
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 输出: 55
装饰器的注意事项
- 调试困难:装饰器会改变函数签名,增加调试复杂度
- 执行顺序:多个装饰器时,最靠近函数的装饰器先执行
- 性能影响:装饰器会增加函数调用的开销
- 文档维护:确保使用
@wraps保留原函数信息
总结
Python装饰器是一个强大而灵活的工具,掌握它可以帮助你写出更优雅、更模块化的代码。从简单的日志记录到复杂的Web框架,装饰器无处不在。建议从简单的单层装饰器开始练习,逐步掌握带参数的装饰器和类装饰器,最终能够在实际项目中灵活运用。
记住:装饰器的核心思想是”不修改原有代码的情况下扩展功能”,这是软件工程中”开闭原则”的完美体现。
