装饰器是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 //m.sbmmt.com/ All Rights Reserved | php.cn | 湘ICP备2023035733号