Python 装饰器完全指南:从入门到精通
什么是装饰器?
装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。装饰器的返回值也是一个函数对象。
在 Python 中,装饰器使用 @decorator_name 的语法糖,这使得代码更加简洁和优雅。
基础装饰器示例
让我们从一个最简单的例子开始:
def my_decorator(func):
def wrapper():
print("在函数执行前做点什么")
func()
print("在函数执行后做点什么")
return wrapper
@my_decorator
def say_hello():
print("你好,世界!")
say_hello()输出结果:
在函数执行前做点什么
你好,世界!
在函数执行后做点什么在这个例子中,my_decorator 是一个装饰器,它接收一个函数作为参数,并返回一个新的函数 wrapper。当我们调用 say_hello() 时,实际上调用的是 wrapper() 函数。
处理带参数的函数
上面的装饰器只能处理没有参数的函数。如果我们需要装饰带有参数的函数,我们需要使用 *args 和 **kwargs 来接收任意参数:
def timing_decorator(func):
def wrapper(*args, **kwargs):
import time
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
@timing_decorator
def calculate_sum(n):
total = 0
for i in range(n):
total += i
return total
result = calculate_sum(1000000)
print(f"结果: {result}")输出结果:
calculate_sum 执行时间: 0.0532 秒
结果: 499999500000保留函数的元信息
当我们使用装饰器时,原始函数的元信息(如 __name__、__doc__ 等)会被包装函数替换。这可能会导致一些问题,比如文档字符串丢失。
Python 提供了 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(3, 5))
print(f"函数名: {add.__name__}")
print(f"文档: {add.__doc__}")输出结果:
调用函数: add
参数: args=(3, 5), kwargs={}
返回值: 8
8
函数名: add
文档: 两个数相加带参数的装饰器
有时我们需要装饰器本身接受参数。这需要创建一个装饰器工厂函数:
def repeat_decorator(times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat_decorator(times=3)
def get_random_number():
import random
return random.randint(1, 100)
numbers = get_random_number()
print(f"随机数列表: {numbers}")输出示例:
随机数列表: [42, 78, 15]类装饰器
除了函数装饰器,Python 还支持类装饰器。类装饰器可以实现更复杂的功能:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"函数 {self.func.__name__} 已被调用 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def process_data(data):
return data * 2
print(process_data(5))
print(process_data(10))输出结果:
函数 process_data 已被调用 1 次
10
函数 process_data 已被调用 2 次
20实际应用场景
1. 缓存装饰器
import functools
def cache_decorator(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args in cache:
print(f"从缓存获取结果: {args}")
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@cache_decorator
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
print(fibonacci(10))2. 权限验证装饰器
def require_permission(permission):
def decorator(func):
def wrapper(*args, **kwargs):
current_user = kwargs.get('user')
if not current_user:
raise PermissionError("未登录用户")
if permission not in current_user.get('permissions', []):
raise PermissionError(f"缺少权限: {permission}")
return func(*args, **kwargs)
return wrapper
return decorator
@require_permission('admin')
def delete_user(user_id, user):
print(f"删除用户: {user_id}")
admin_user = {'permissions': ['admin', 'read']}
delete_user(123, user=admin_user)多个装饰器的叠加
我们可以为一个函数应用多个装饰器,装饰器的执行顺序是从下到上:
def decorator1(func):
def wrapper():
print("装饰器 1 - 前")
func()
print("装饰器 1 - 后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器 2 - 前")
func()
print("装饰器 2 - 后")
return wrapper
@decorator1
@decorator2
def my_function():
print("执行函数")
my_function()输出结果:
装饰器 1 - 前
装饰器 2 - 前
执行函数
装饰器 2 - 后
装饰器 1 - 后最佳实践
1. 使用 functools.wraps:保留原始函数的元信息
2. 保持装饰器的简洁性:每个装饰器只做一件事
3. 处理异常:在装饰器中适当处理异常
4. 使用类型提示:提高代码可读性
总结
装饰器是 Python 中一个非常强大的特性,它让我们能够:
- 在不修改原有代码的情况下添加功能
- 提高代码的可重用性
- 实现横切关注点(如日志、缓存、权限验证)
掌握装饰器将帮助你写出更加优雅、简洁和可维护的 Python 代码。开始在你的项目中使用装饰器吧!
