Python 注解 @以及 django 和 flask 的使用疑问

2018-12-03 18:13:24 +08:00
 rizon

本人 java 开发最近自学 python,web 项目框架

flask 使用了注解来注册路径

@app.route("/")
def hello():
    return "Hello World!"

django 中则是

urlpatterns = [
    path('admin/', admin.site.urls)
]

问题 1:
flask 中注解是怎么实现注册的?注解的代码是在方法被调用的时候才会触发吧?那么他是怎么在项目初始化的时候注册映射的呢?

问题 2:
我喜欢注解,就想着是不是可以自己写个注解来让 django 也可以使用注解注册?其实解答了疑问 1,理论上问题 2 也就回答了吧。

3792 次点击
所在节点    Python
24 条回复
neoblackcap
2018-12-03 18:19:29 +08:00
那个不是注解,Python 没有注解,那个是装饰器,要打比方也是高阶函数。
跟 Java 注解对 bytecode 下手不一样
vissssa
2018-12-03 18:21:37 +08:00
本质上是注册方法的简写
> self.add_url_rule(rule, endpoint, f, **options)
rizon
2018-12-03 18:22:35 +08:00
@neoblackcap #1 嗯,是的,我也发现我理解有误,python 里面这个叫做 Decorators 的东西,其实就是在函数外面包装了一层或多层函数?
那么 flask 是怎么利用这个东西实现的呢?还在翻源码研究,,
rizon
2018-12-03 18:24:21 +08:00
@vissssa #2 是,可是我不理解的是,这个 app.route() 方法是什么时候会被触发?我理解的是,python 的修饰器是在方法被执行的时候才会执行吧?
misaka19000
2018-12-03 18:41:28 +08:00
装饰器不是在方法执行的才会执行,装饰器本质上是个函数

举个例子
def a():
pass

@a
def b():
pass

虽然然 b 方法没有执行,但是此时 a 方法也就是装饰器已经被执行了
hahastudio
2018-12-03 18:47:38 +08:00
wwqgtxx
2018-12-03 19:11:28 +08:00
@app.route("/")
def hello():
----return "Hello World!"
这个代码等价于
def hello():
----return "Hello World!"
hello = app.route("/")(hello)
whusnoopy
2018-12-03 19:11:44 +08:00
@rizon 是这个文件被引用的时候就触发展开了,如果文件没被引用,一样不会被触发
XIVN1987
2018-12-03 19:12:47 +08:00
我觉得就是在执行的时候注册的,,

没读过 CPython 的源码,不过读过 micropython 的源码,,感觉原理应该是类似的

micropython 解释器遇到 def hello()语句时,就会执行指令 MP_BC_MAKE_FUNCTION,,我觉得应该也是在执行的时候遇到 @app.route("/")语句时执行相应的字节码完成注册的

至于装饰器语法本身,可以看看《 Fluent Python 》(中文翻译《流畅的 Python 》),,这本书对 Python 的高级语法讲解的非常好
XIVN1987
2018-12-03 19:19:05 +08:00
@whusnoopy
import 本身就会执行被 import 的文件,,
你在 hello.py 文件里写个 print 'hello',,然后在 main.py 里 import hello,,会打印出 hello 的
rizon
2018-12-03 20:17:33 +08:00
@wwqgtxx #7 虽然代码等价过来,但是后面的`(hello)`不还是没有执行吗?那怎么注册的?
rizon
2018-12-03 20:18:19 +08:00
@XIVN1987 #9 可是我自己一个修饰器,为什么就不能起到同样的效果?
rizon
2018-12-03 20:19:36 +08:00
@hahastudio #6 谢谢,看了,可还是不能理解。我 debug 代码的时候

```python
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
```
这个方法被执行了,但是为什么会被执行还是不能理解。
neoblackcap
2018-12-03 20:37:46 +08:00
@rizon 因为你后面再输入`(hello)`的时候已经不是原来的函数执行了,python 一个文件即是一个模块,模块被引用的时候,会解释执行里面的代码,函数声明不会被触发。但是上面已经说了

```python
@app.route("/")
def hello():
----return "Hello World!"
这个代码等价于
def hello():
----return "Hello World!"
hello = app.route("/")(hello) <- 这里才是关键,这里代码已经被解释执行了!在这里面路由已经完成了注册
```

假如你的 flask 项目是分模块的,如果不引入对应的模块,以及对应的视图函数,那么路由就会注册不成功。
HelloAmadeus
2018-12-03 20:43:21 +08:00
装饰器是一个函数,只接受一个参数,返回一个函数,用 @装饰函数,在 import 模块时候运行一次。用处嘛,就大概是 java 里面向切面编程,将一些通用的逻辑插到函数里。
flask 那个 app.route 装饰器就是在你 import 的时候,注册好了路由
huadi
2018-12-03 21:00:03 +08:00
你用百度的吧? Google 上 python decorator 关键字很容易就找到答案了啊!
lniwn
2018-12-03 21:09:19 +08:00
@neoblackcap 小小的纠正下,不能说 python 没有注解,python 的注解是用来标注函数参数和返回值类型的,供 ide 做静态分析用,可选。
freakxx
2018-12-03 21:35:37 +08:00
flask, django 也是 mvc 模式,
你看到的 route 或者 urlpatterns 都可以看成是 c 这一层。

如果要看源码 直接去 看下 app.py 1224

```
@app.route("/")
def hello():
return "Hello World!"
```
self.add_url_rule(rule, endpoint, f, **options)

将路径和视图绑定了起来,这个主要是做一个 map 处理。

----------------

@是修饰器,python 这边的语法糖。
rizon
2018-12-03 21:36:51 +08:00
@neoblackcap #14
@wwqgtxx #7
这事我终于整明白了,带参数的修饰器和不带参数的修饰器是不一样的。。
带参数修饰器会在初始化时就执行修饰器的代码并将方法体重新赋值给方法名。
不带参数修饰器在初始化的时候会将方法名赋值给修饰器方法,修饰器方法内部来手动调用被修饰的方法。
wwqgtxx
2018-12-03 21:39:00 +08:00
@rizon 你要理解 python 中函数是一类对象,一个函数是可以返回另一个函数作为返回值的,所以后面的(hello)并不是一个声明,而是一个链式函数调用,即 app.route('/')返回了一个函数,而后面的(hello)是用 hello 作为参数传递给个该函数,最后结果是再返回一个函数赋值给 hello,当然在 app.route 的这个地方,最后 hello 是没变的,但是实际上也可以完全不返回原函数,而返回另一个

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/513929

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX