引言
Python 装饰器是一种强大的工具,用于在不修改函数代码的情况下为其添加额外功能。它广泛应用于日志记录、性能监控、权限校验等场景,能有效减少重复代码,提高代码的可维护性和可读性。
装饰器基础
函数是一等公民
在 Python 中,函数是对象,可以赋值给变量、作为参数传递或作为返回值返回。例如:
python
def greet(name):
return f"Hello, {name}!"
say_hello = greet
print(say_hello("Alice")) # Hello, Alice!
闭包
闭包是指内部函数引用了外部函数的变量,即使外部函数已执行完毕,这些变量依然可以被内部函数访问。例如:
python
def make_multiplier(n):
def multiplier(x):
return x * n
return multiplier
times2 = make_multiplier(2)
print(times2(5)) # 10
装饰器的基本语法
装饰器是一个函数,接收另一个函数作为参数,并返回一个新的函数。使用 @
语法糖可以使装饰器的使用更加简洁。
基础装饰器
python
def simple_decorator(func):
def wrapper():
print("Before the function runs")
func()
print("After the function runs")
return wrapper
@simple_decorator
def say_hello():
print("Hello!")
say_hello()
输出:
text
Before the function runs
Hello!
After the function runs
通用装饰器
对于带参数的函数,装饰器需要使用 *args
和 **kwargs
来接收任意参数:
python
def universal_decorator(func):
def wrapper(*args, **kwargs):
print("Function is running...")
result = func(*args, **kwargs)
print("Function finished")
return result
return wrapper
@universal_decorator
def add(a, b):
return a + b
print(add(3, 5))
常见场景与实战示例
日志与调试
python
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_decorator
def multiply(a, b):
return a * b
multiply(3, 4)
性能监控
python
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f} seconds")
return result
return wrapper
@timing_decorator
def slow_function():
time.sleep(1)
return "done"
slow_function()
权限校验
python
def require_admin(func):
def wrapper(user, *args, **kwargs):
if not user.get("is_admin"):
raise PermissionError("Admin access required")
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_database(user):
return "Database deleted!"
admin = {"name": "Alice", "is_admin": True}
guest = {"name": "Bob", "is_admin": False}
print(delete_database(admin)) # 正常执行
# print(delete_database(guest)) # 抛出 PermissionError
缓存
python
def cache_decorator(func):
cache = {}
def wrapper(*args):
if args in cache:
print("Returning cached result")
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)) # 第二次命中缓存
进阶内容
保留函数元信息:functools.wraps
使用 functools.wraps
可以保留原函数的元信息,如 __name__
和 __doc__
:
python
from functools import wraps
def simple_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@simple_decorator
def greet(name):
"""Say hello to someone"""
return f"Hello, {name}!"
print(greet.__name__) # greet
print(greet.__doc__) # Say hello to someone
类装饰器
可以用类实现装饰器,通过定义 __call__
方法让类实例可调用:
python
class Repeat:
def __init__(self, times):
self.times = times
def __call__(self, func):
def wrapper(*args, **kwargs):
result = None
for _ in range(self.times):
result = func(*args, **kwargs)
return result
return wrapper
@Repeat(3)
def say_hi(name):
print(f"Hi, {name}!")
say_hi("Alice")
装饰器叠加
多个装饰器的执行顺序是从内到外,即离函数最近的装饰器先执行:
python
def decorator_a(func):
def wrapper(*args, **kwargs):
print("Decorator A before")
result = func(*args, **kwargs)
print("Decorator A after")
return result
return wrapper
def decorator_b(func):
def wrapper(*args, **kwargs):
print("Decorator B before")
result = func(*args, **kwargs)
print("Decorator B after")
return result
return wrapper
@decorator_a
@decorator_b
def say_hello():
print("Hello!")
say_hello()
带参数的装饰器工厂
可以通过多层函数实现带参数的装饰器:
python
from functools import wraps
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
实用库中的装饰器
标准库中的装饰器
@staticmethod
:定义静态方法。@classmethod
:定义类方法。@property
:将方法变为只读属性。@functools.lru_cache
:缓存函数结果。
第三方框架中的装饰器
- Flask 的
@app.route
:将函数注册为路由处理器。 - Django 的
@login_required
:限制只有登录用户才能访问视图。 - Click 的命令行装饰器:定义命令和参数。
最佳实践与注意事项
避免滥用
装饰器适用于横切逻辑,避免过度使用,以免降低代码可读性。
始终使用 functools.wraps
使用 functools.wraps
保留原函数的元信息,便于调试和文档生成。
注意副作用
装饰器可能会改变函数的调用方式或引入缓存、权限等逻辑,需注意其副作用。
保持可读性
复杂的装饰器逻辑应清晰注释,必要时可用类装饰器或明确的函数调用替代。
关注性能
装饰器会增加函数调用层级,复杂逻辑或过多装饰器可能影响性能。必要时使用性能分析工具检查。
总结
装饰器是 Python 中一种强大的工具,能有效分离横切逻辑,提高代码的可读性和扩展性。掌握其基础语法、进阶用法及最佳实践,可在实际开发中高效利用装饰器,写出更优雅的代码。