装饰器是Python中用于包装或修改函数、方法或类行为的高阶函数,无需修改原代码即可添加日志、计时、权限校验等横切关注点。其核心语法为@decorator_name,本质是将函数作为参数传入装饰器并返回新函数。使用functools.wraps可保留原函数元信息,避免调试困难。带参数的装饰器需多一层嵌套结构,如@log_level(level="DEBUG")。装饰器解决了代码重复和关注点分离问题,广泛应用于Web路由(@app.route)、权限控制(@login_required)、限流、缓存(@lru_cache)和重试机制等场景,提升代码模块化与可维护性。最佳实践包括单一职责、通用性设计、异常处理和避免滥用。

Python中的装饰器,说白了,就是一种特殊类型的函数,它的核心作用是用来包装或修改另一个函数、方法或类的行为,而不需要直接改动被包装对象的源代码。你可以把它想象成给一个函数穿上了一件外套,这件外套可以在函数执行前后添加额外的功能,比如日志记录、性能计时、权限检查等等。它让我们的代码变得更“干净”,更模块化,尤其是在处理那些跨越多个功能模块的“横切关注点”时,简直是神器。
要理解装饰器怎么用,我们得从最基础的语法和它背后的原理说起。
最常见的装饰器用法,就是直接在函数定义上方加上
@decorator_name
def original_function():
pass
# 等价于
original_function = decorator_name(original_function)所以,装饰器本身就是一个接受函数作为参数,并返回一个新函数(通常是内部定义的
wrapper
立即学习“Python免费学习笔记(深入)”;
我们来看一个最简单的例子,用来记录函数执行的日志:
import functools
def log_calls(func):
# functools.wraps 是个好东西,它能把原函数的元信息(比如名字、文档字符串)复制到wrapper函数上
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"INFO: Calling function '{func.__name__}' with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"INFO: Function '{func.__name__}' finished, returned: {result}")
return result
return wrapper
@log_calls
def add(a, b):
"""计算两个数的和。"""
print(f"Inside add: {a} + {b}")
return a + b
@log_calls
def subtract(x, y):
"""计算两个数的差。"""
print(f"Inside subtract: {x} - {y}")
return x - y
# 使用被装饰的函数
sum_result = add(10, 5)
print(f"Sum result: {sum_result}\n")
diff_result = subtract(y=3, x=7)
print(f"Diff result: {diff_result}")
print(f"Name of decorated add function: {add.__name__}") # 输出 'add' 而不是 'wrapper',多亏了 @functools.wraps
print(f"Docstring of decorated add function: {add.__doc__}")在这个例子里,
log_calls
add
subtract
wrapper
wrapper
log_calls
wrapper
如果你需要装饰器本身也接收参数,比如你想控制日志的级别,那就需要再多一层嵌套:
import functools
def log_level(level="INFO"):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] Calling '{func.__name__}'...")
result = func(*args, **kwargs)
print(f"[{level}] Finished '{func.__name__}'.")
return result
return wrapper
return decorator
@log_level(level="DEBUG")
def process_data(data):
"""处理一些数据。"""
print(f"Processing: {data}")
return f"Processed: {data}"
@log_level() # 不传参数,使用默认的INFO级别
def fetch_config(key):
"""获取配置项。"""
print(f"Fetching config for {key}")
return {"key": key, "value": "some_value"}
process_data([1, 2, 3])
fetch_config("database_url")这里
log_level
level
decorator
decorator
wrapper
我个人觉得,装饰器最核心的价值,在于它提供了一种优雅的方式来处理“横切关注点”(Cross-Cutting Concerns)。什么是横切关注点?简单来说,就是那些散布在程序中多个模块,但又不属于任何一个模块核心业务逻辑的功能。最典型的就是日志、性能监控、事务管理、权限校验、缓存等。
想象一下,如果你有几十个甚至上百个函数都需要添加日志记录。如果没有装饰器,你可能得在每个函数的开头和结尾手动插入日志代码。这不仅会让代码变得臃肿、难以阅读,而且一旦日志逻辑需要修改(比如从打印到文件改成打印到数据库),你得修改所有这些地方。这简直是维护者的噩梦,也极大地增加了出错的风险。
装饰器就像一个“即插即用”的插件系统。它把这些横切关注点从核心业务逻辑中剥离出来,让你的业务函数只专注于它应该做的事情。比如一个计算函数就只管计算,至于计算前后的日志、计时,那是装饰器的事情。这种分离关注点的设计,让代码更具可读性、可维护性和可扩展性。我记得刚开始接触Python时,看到
@
在自己动手写装饰器的时候,我踩过不少坑,也总结了一些经验。
一个最常见的陷阱就是忘记使用
functools.wraps
__name__
__doc__
add.__name__
@functools.wraps
wrapper
add
@functools.wraps(func)
wrapper
另一个我常犯的错误,是在
wrapper
def wrapper(arg1, arg2):
*args
**kwargs
至于最佳实践,我觉得有几点特别重要:
log_level
wrapper
try...except
装饰器这玩意儿,一旦你掌握了它,会发现它在实际项目中简直无处不在,而且能解决很多“头疼”的问题。
最直观的,就是各种Web框架里的应用。比如Flask或Django,
@app.route('/path')@login_required
@permission_required('admin')再比如API限流。如果你有一个对外开放的API,为了防止恶意请求或者系统过载,你可能需要限制某个用户或某个IP在一定时间内的请求次数。这时,你可以写一个
@rate_limit(calls_per_minute=10)
缓存(Memoization)也是一个非常棒的应用场景。Python标准库里的
functools.lru_cache
@functools.lru_cache()
还有**重试
以上就是Python中装饰器怎么用 Python中装饰器使用指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号