FastAPI初识&Python 装饰器学习

1026 字
5 分钟
FastAPI初识&Python 装饰器学习

从 FastAPI 的 @app.get() 出发,拆开看看 Python 装饰器到底在干什么。

前言#

我一开始是从 FastAPI 的 @app.get() 产生疑问,然后顺着“它到底在修饰谁”“为什么这样写”“装饰器什么时候执行”“带参数装饰器又是什么”这条线,一步一步把 Python 装饰器弄懂。

这篇整理记录的是我的学习过程,先把思路走通,再回头看结论。


1. 最初疑问:FastAPI 路由装饰器#

其中的代码:

@app.get("/api/profile")
def get_profile():
return profile

疑问点 1:@app.get() 和下方 def 是不是绑定在一起?#

结论:是的,装饰器只修饰紧随其后的函数。它们在语法上必须连在一起,中间不能插入其他代码。

疑问点 2:谁装饰谁?#

@app.get("/api/profile") 装饰的是函数 get_profile

疑问点 3:装饰器到底有什么用?#

它的作用是给普通函数打上“角色标记”,告诉框架:

这个函数用来处理 GET /api/profile 的网络请求。

等价的原生写法可以理解为:

def get_profile():
return profile
app.add_api_route(path="/api/profile", endpoint=get_profile, methods=["GET"])

所以装饰器本质上是语法糖:把“路径注册”和“业务函数”写在一起,让代码更紧凑、更易读。


2. 切入基础:自定义装饰器(计时器示例)#

示例代码:

import time
def timer(function):
def wrapper():
time_start = time.time()
function()
time_end = time.time()
cost_time = time_end - time_start
print("花费时间:{}秒".format(cost_time))
return wrapper
@timer
def func1():
print('我是一个函数1')

关键疑问 1:函数内部为什么还能定义函数?#

Python 支持嵌套函数。内层函数可以使用外层函数传进来的变量,这就是闭包的典型表现。

这里可以这样拆:

  • timer:外层工厂函数
  • wrapper:包装函数,负责新增逻辑(计时)

关键疑问 2:看不见 wrapper() 调用,它是怎么运行的?#

把语法糖展开:

@timer
def func1():
pass
# 等价于
def func1():
pass
func1 = timer(func1)

执行过程可以分成三步:

  1. 代码加载阶段(定义时):执行 timer(原始 func1),生成 wrapper 函数对象;
  2. 变量覆盖:名字 func1 不再指向原始函数,而是指向 wrapper
  3. 调用阶段:执行 func1(),实际上就是执行 wrapper()

关键疑问 3:为什么是 return wrapper,不能写 return wrapper()#

这两者完全不同:

  • return wrapper:返回函数对象本身,后续可以随时调用;
  • return wrapper():立刻执行函数,返回执行结果(大多是 None)。

错误示范:

def deco(func):
def inner():
print("包装启动")
func()
return inner() # 当场执行 inner,返回 None
@deco
def demo():
print("demo执行")
demo() # 报错 TypeError: 'NoneType' object is not callable

3. 随堂测试复盘:易错的时序考点#

考题 1#

def deco(func):
def inner():
print("开始")
func()
print("结束")
return inner
@deco
def hello():
print("Hello")
hello()

输出:

开始
Hello
结束

理解方式:hello = deco(hello),调用 hello() 时实际执行的是 inner()

考题 2:时序大坑#

def deco(func):
print("进入deco")
def inner():
print("inner运行")
func()
return inner
@deco
def test():
print("执行test本体")
print("====分割线====")
test()

正确输出顺序:

进入deco
====分割线====
inner运行
执行test本体

核心知识点:@ 装饰器在函数定义阶段就执行外层代码,不等你调用函数。


4. 知识迁移到 FastAPI#

回到最开始的代码:

@app.get("/api/profile")
def get_profile():
return profile

它可以展开成:

def get_profile():
return profile
# 1. app.get 先执行,得到一个装饰器
decorator = app.get("/api/profile")
# 2. 使用装饰器包装原始函数
get_profile = decorator(get_profile)

带参数的装饰器:#

  • app.get("路径") 先传入配置;
  • 然后返回一个专属装饰器;
  • 再由这个装饰器去包装业务函数。

执行流程#

  1. 项目启动加载代码:只是在注册路由表;
  2. 浏览器发起 HTTP 请求访问接口时,FastAPI 自动调用包装后的函数;
  3. 框架自动把 return 的字典转成 JSON,并封装成 HTTP 响应。

我最后记住的是,装饰器的核心就是 @deco 等价于 f = deco(f)。它不是在改原函数的代码,而是把原函数交给另一个函数包一层,再把新的函数返回回来。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
FastAPI初识&Python 装饰器学习
https://hp-patience.github.io/posts/fastapi-python-decorator-learning/
作者
Celyn
发布于
2026-08-15
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Profile Image of the Author
Celyn
记录 AI、前后端与自动化学习,用费曼学习法把知识讲清楚。
公告
欢迎来到 Celyn 的博客!专注于AI大模型、深度学习与编程技术分享。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
19
分类
12
标签
47
总字数
45,991
运行时长
0
最后活动
0 天前

文章目录