理解 Python 装饰器:@ 的强大功能
文章目录
- 理解 Python 装饰器:`@` 的强大功能
- 什么是装饰器?
- 装饰器何时执行?
- 装饰器实战:FastAPI
- FastAPI 的 `@app.get`
- FastAPI 的 `@app.on_event`
- 关于装饰器的关键点
- 结论
理解 Python 装饰器:@
的强大功能
在 Python 中,@
符号与**装饰器(Decorator)**紧密相关。装饰器是一种强大且灵活的工具,允许在不修改函数、方法或类源代码的情况下,扩展或修改其行为。本文将深入探讨装饰器的工作原理、执行时机以及实际应用,结合通用 Python 编程和 FastAPI 框架的示例。
什么是装饰器?
装饰器是一个高阶函数(或可调用对象),通过包装另一个函数或类来添加功能。Python 的 @
符号是装饰器的语法糖,用于简洁地应用装饰器。例如:
def my_decorator(func):def wrapper():print("函数调用前")func()print("函数调用后")return wrapper@my_decorator
def say_hello():print("你好!")say_hello()
输出:
函数调用前
你好!
函数调用后
在这里,@my_decorator
将 say_hello
函数包装在一个 wrapper
函数中,添加了调用前后打印日志的功能。@
语法等价于:
say_hello = my_decorator(say_hello)
装饰器何时执行?
装饰器的执行分为两个阶段:
- 代码解析阶段(模块加载时):
- 当 Python 加载模块时,遇到
@decorator
,会立即执行装饰器函数(例如my_decorator
),将目标函数(例如say_hello
)作为参数传递,并返回一个新的函数(例如wrapper
)。 - 这一阶段负责装饰器的注册或配置。
- 示例:
- 当 Python 加载模块时,遇到
import time
from functools import wrapsdef timer_decorator(func):print("解析时执行:注册装饰器")@wraps(func)def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)print(f"{func.__name__} 耗时 {time.time() - start_time:.2f} 秒")return resultreturn wrapper@timer_decorator
def slow_function():time.sleep(1)return "完成"print("正在解析代码...")
slow_function()
输出:
解析时执行:注册装饰器
正在解析代码...
slow_function 耗时 1.00 秒
print("解析时执行:注册装饰器")
在模块加载时打印,表明装饰器的外层逻辑在解析时执行。
- 运行时阶段:
- 被包装的函数(
wrapper
)只有在调用被装饰函数(例如slow_function()
)时才会执行。 - 在上述示例中,
wrapper
的计时逻辑(记录时间并打印)在slow_function()
调用时运行。
- 被包装的函数(
装饰器实战:FastAPI
装饰器在 FastAPI 框架中被广泛用于路由和事件处理。以下是具体分析。
FastAPI 的 @app.get
在 FastAPI 中,@app.get
是一个装饰器,用于将函数绑定到特定的 HTTP 路由。例如:
from fastapi import FastAPIapp = FastAPI()@app.get("/hello")
@app.get("/v1/hello")
async def hello():return {"message": "你好!"}
- 解析阶段:
- Python 解析代码时,
@app.get("/hello")
和@app.get("/v1/hello")
调用FastAPI.get
方法,进而调用app.add_api_route
,将hello
函数注册到app.routes
路由表,分别对应/hello
和/v1/hello
。 - 此时没有处理任何 HTTP 请求,装饰器仅完成路由配置。
- Python 解析代码时,
- 运行时阶段:
- 当收到
GET /hello
或GET /v1/hello
请求时,FastAPI 查询app.routes
,找到匹配的hello
函数并直接调用。装饰器的逻辑(路由注册)不会再次执行。
- 当收到
- 多个装饰器:
- 多个
@app.get
装饰器按从下到上的顺序应用:@app.get("/v1/hello")
将hello
注册到/v1/hello
。@app.get("/hello")
将hello
注册到/hello
。- 两个路由都指向同一个
hello
函数,请求任一路径都会触发它。
- 多个
FastAPI 的 @app.on_event
FastAPI 曾使用 @app.on_event
装饰器来处理启动或关闭事件。例如:
@app.on_event("startup")
async def startup_event():print("应用启动...")
- 解析阶段:
@app.on_event("startup")
调用FastAPI.on_event
,将startup_event
注册到启动事件处理列表。 - 运行时阶段:应用启动时,FastAPI 调用所有注册的启动事件函数。
注意:
@app.on_event
在 FastAPI 新版本中已废弃,推荐使用lifespan
机制。例如:
from contextlib import asynccontextmanager@asynccontextmanager
async def lifespan(app: FastAPI):print("应用启动...")yieldprint("应用关闭...")app = FastAPI(lifespan=lifespan)
关于装饰器的关键点
- 不限于函数:装饰器可以应用于函数、方法或类,只要它们是可调用对象(类需实现
__call__
)。 - 多个装饰器:多个装饰器按从下到上的顺序应用(由内向外)。例如:
@decorator1
@decorator2
def my_function():print("函数执行")
-
decorator2
先包装my_function
,decorator1
再包装decorator2
的结果。 -
解析与运行时:
- 解析时:执行装饰器的外层逻辑(注册、配置)。
- 运行时:执行包装逻辑(例如计时、路由处理)。
-
FastAPI 特性:
@app.get
在解析时注册路由(调用app.add_api_route
),运行时直接调用目标函数。
结论
Python 的 @
装饰器是一种优雅的工具,能够增强代码的模块化和可重用性。无论是记录函数执行时间、注册 FastAPI 路由,还是实现权限控制,装饰器都提供了简洁的方式在不修改原始代码的情况下添加功能。