Python 中注解 @ 的用法及在 Django 和 Flask 中的使用疑问
本人 java 开发最近自学 python,web 项目框架
flask 使用了注解来注册路径
@app.route("/")
def hello():
return "Hello World!"
django 中则是
urlpatterns = [
path('admin/', admin.site.urls)
]
问题 1:
flask 中注解是怎么实现注册的?注解的代码是在方法被调用的时候才会触发吧?那么他是怎么在项目初始化的时候注册映射的呢?
问题 2:
我喜欢注解,就想着是不是可以自己写个注解来让 django 也可以使用注解注册?其实解答了疑问 1,理论上问题 2 也就回答了吧。
Python 中注解 @ 的用法及在 Django 和 Flask 中的使用疑问
那个不是注解,Python 没有注解,那个是装饰器,要打比方也是高阶函数。
跟 Java 注解对 bytecode 下手不一样
在Python里,@符号是用来表示装饰器的语法糖。它本质上是一个函数,用来修改另一个函数或类的行为,而不用直接修改其源代码。你可以把它想象成给函数“包了一层”或“打了个标签”。
基本用法:
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后
这里,@my_decorator 等同于 say_hello = my_decorator(say_hello)。
在Web框架中的应用:
-
Django: 主要用
@来做视图的访问控制或请求预处理。@login_required: 用户必须登录才能访问这个视图。@csrf_exempt: 免除这个视图的CSRF保护。
from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt @login_required def my_view(request): return HttpResponse("只有登录用户能看到") @csrf_exempt def api_endpoint(request): return HttpResponse("这个端点不需要CSRF token") -
Flask:
@是定义路由的核心,用来把URL映射到处理函数。@app.route(): 定义路由规则。@app.before_request: 在每个请求之前执行的函数。
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return '首页' @app.route('/user/<username>') def show_user(username): return f'用户:{username}' @app.before_request def before_each_request(): print("每个请求开始前都会执行这里")
核心区别:
- Django的装饰器大多是功能性的(权限、缓存、CSRF等),框架已经为你写好了,你主要是“使用”它们。
- Flask的
@app.route()是声明式的,是框架运作的基石,用来告诉Flask“哪个函数处理哪个URL”。
简单说,Python的 @ 是装饰器,在Django里常用作“功能开关”,在Flask里是“路由定义器”。
本质上是注册方法的简写
> self.add_url_rule(rule, endpoint, f, **options)
#1 嗯,是的,我也发现我理解有误,python 里面这个叫做 Decorators 的东西,其实就是在函数外面包装了一层或多层函数?
那么 flask 是怎么利用这个东西实现的呢?还在翻源码研究,,
#2 是,可是我不理解的是,这个 app.route() 方法是什么时候会被触发?我理解的是,python 的修饰器是在方法被执行的时候才会执行吧?
装饰器不是在方法执行的才会执行,装饰器本质上是个函数
举个例子
def a():
pass
def b():
pass
虽然然 b 方法没有执行,但是此时 a 方法也就是装饰器已经被执行了
.route("/")
def hello():
----return "Hello World!"
这个代码等价于
def hello():
----return “Hello World!“
hello = app.route(”/”)(hello)
是这个文件被引用的时候就触发展开了,如果文件没被引用,一样不会被触发
我觉得就是在执行的时候注册的,,
没读过 CPython 的源码,不过读过 micropython 的源码,,感觉原理应该是类似的
micropython 解释器遇到 def hello()语句时,就会执行指令 MP_BC_MAKE_FUNCTION,,我觉得应该也是在执行的时候遇到 .route("/")语句时执行相应的字节码完成注册的
至于装饰器语法本身,可以看看《 Fluent Python 》(中文翻译《流畅的 Python 》),,这本书对 Python 的高级语法讲解的非常好
#7 虽然代码等价过来,但是后面的(hello)不还是没有执行吗?那怎么注册的?
#9 可是我自己一个修饰器,为什么就不能起到同样的效果?
#6 谢谢,看了,可还是不能理解。我 debug 代码的时候python<br> def decorator(f):<br> endpoint = options.pop('endpoint', None)<br> self.add_url_rule(rule, endpoint, f, **options)<br> return f<br>
这个方法被执行了,但是为什么会被执行还是不能理解。
因为你后面再输入(hello)的时候已经不是原来的函数执行了,python 一个文件即是一个模块,模块被引用的时候,会解释执行里面的代码,函数声明不会被触发。但是上面已经说了python<br>.route("/")<br>def hello():<br>----return "Hello World!"<br>这个代码等价于<br>def hello():<br>----return "Hello World!"<br>hello = app.route("/")(hello) <- 这里才是关键,这里代码已经被解释执行了!在这里面路由已经完成了注册<br>
假如你的 flask 项目是分模块的,如果不引入对应的模块,以及对应的视图函数,那么路由就会注册不成功。
装饰器是一个函数,只接受一个参数,返回一个函数,用 @装饰函数,在 import 模块时候运行一次。用处嘛,就大概是 java 里面向切面编程,将一些通用的逻辑插到函数里。
flask 那个 app.route 装饰器就是在你 import 的时候,注册好了路由
你用百度的吧? Google 上 python decorator 关键字很容易就找到答案了啊!
小小的纠正下,不能说 python 没有注解,python 的注解是用来标注函数参数和返回值类型的,供 ide 做静态分析用,可选。
flask, django 也是 mvc 模式,
你看到的 route 或者 urlpatterns 都可以看成是 c 这一层。
如果要看源码 直接去 看下 app.py 1224<br>.route("/")<br>def hello():<br> return "Hello World!"<br>
self.add_url_rule(rule, endpoint, f, **options)
将路径和视图绑定了起来,这个主要是做一个 map 处理。
----------------
是修饰器,python 这边的语法糖。
#14
#7
这事我终于整明白了,带参数的修饰器和不带参数的修饰器是不一样的。。
带参数修饰器会在初始化时就执行修饰器的代码并将方法体重新赋值给方法名。
不带参数修饰器在初始化的时候会将方法名赋值给修饰器方法,修饰器方法内部来手动调用被修饰的方法。
你要理解 python 中函数是一类对象,一个函数是可以返回另一个函数作为返回值的,所以后面的(hello)并不是一个声明,而是一个链式函数调用,即 app.route(’/’)返回了一个函数,而后面的(hello)是用 hello 作为参数传递给个该函数,最后结果是再返回一个函数赋值给 hello,当然在 app.route 的这个地方,最后 hello 是没变的,但是实际上也可以完全不返回原函数,而返回另一个
刚看到你 19#的回复,再说一下,传递的永远是个 function object 而不是什么“方法名”,只不过“带参数修饰器”本身需要返回一个函数,这个函数接受一个 callable 对象,而“不带参数修饰器”直接就是上面说的那个函数
#22 嗯嗯。是的,是这样的。谢谢。
@符号在 java 中是注解功能并不是实质功能,但是在 python 中 @后面跟的是装饰器,其实就是一个高阶函数,对下面的函数装饰,过程是把下面的函数当参数处理一下再输出一个处理过的函数。
python 程序的执行过程是先把源码编译成字节码,然后再用解释器解释执行字节码。
python 装饰器不是执行时调用的,而是在编译字节码的过程中就调用处理了,实际上相当于语法糖。

