V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
rizon
V2EX  ›  Python

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

  •  
  •   rizon ·
    othorizon · 2018-12-03 18:13:24 +08:00 · 3886 次点击
    这是一个创建于 2173 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    flask 使用了注解来注册路径

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

    django 中则是

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

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

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

    第 1 条附言  ·  2018-12-03 21:42:15 +08:00
    这事我整明白了,总结见帖子 /t/513981
    第 2 条附言  ·  2018-12-03 21:42:31 +08:00
    谢谢大家的帮助~~
    24 条回复    2018-12-03 22:00:07 +08:00
    neoblackcap
        1
    neoblackcap  
       2018-12-03 18:19:29 +08:00 via iPhone
    那个不是注解,Python 没有注解,那个是装饰器,要打比方也是高阶函数。
    跟 Java 注解对 bytecode 下手不一样
    vissssa
        2
    vissssa  
       2018-12-03 18:21:37 +08:00
    本质上是注册方法的简写
    > self.add_url_rule(rule, endpoint, f, **options)
    rizon
        3
    rizon  
    OP
       2018-12-03 18:22:35 +08:00
    @neoblackcap #1 嗯,是的,我也发现我理解有误,python 里面这个叫做 Decorators 的东西,其实就是在函数外面包装了一层或多层函数?
    那么 flask 是怎么利用这个东西实现的呢?还在翻源码研究,,
    rizon
        4
    rizon  
    OP
       2018-12-03 18:24:21 +08:00
    @vissssa #2 是,可是我不理解的是,这个 app.route() 方法是什么时候会被触发?我理解的是,python 的修饰器是在方法被执行的时候才会执行吧?
    misaka19000
        5
    misaka19000  
       2018-12-03 18:41:28 +08:00
    装饰器不是在方法执行的才会执行,装饰器本质上是个函数

    举个例子
    def a():
    pass

    @a
    def b():
    pass

    虽然然 b 方法没有执行,但是此时 a 方法也就是装饰器已经被执行了
    wwqgtxx
        7
    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
        8
    whusnoopy  
       2018-12-03 19:11:44 +08:00
    @rizon 是这个文件被引用的时候就触发展开了,如果文件没被引用,一样不会被触发
    XIVN1987
        9
    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
        10
    XIVN1987  
       2018-12-03 19:19:05 +08:00
    @whusnoopy
    import 本身就会执行被 import 的文件,,
    你在 hello.py 文件里写个 print 'hello',,然后在 main.py 里 import hello,,会打印出 hello 的
    rizon
        11
    rizon  
    OP
       2018-12-03 20:17:33 +08:00
    @wwqgtxx #7 虽然代码等价过来,但是后面的`(hello)`不还是没有执行吗?那怎么注册的?
    rizon
        12
    rizon  
    OP
       2018-12-03 20:18:19 +08:00
    @XIVN1987 #9 可是我自己一个修饰器,为什么就不能起到同样的效果?
    rizon
        13
    rizon  
    OP
       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
        14
    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
        15
    HelloAmadeus  
       2018-12-03 20:43:21 +08:00 via Android
    装饰器是一个函数,只接受一个参数,返回一个函数,用 @装饰函数,在 import 模块时候运行一次。用处嘛,就大概是 java 里面向切面编程,将一些通用的逻辑插到函数里。
    flask 那个 app.route 装饰器就是在你 import 的时候,注册好了路由
    huadi
        16
    huadi  
       2018-12-03 21:00:03 +08:00
    你用百度的吧? Google 上 python decorator 关键字很容易就找到答案了啊!
    lniwn
        17
    lniwn  
       2018-12-03 21:09:19 +08:00 via iPhone
    @neoblackcap 小小的纠正下,不能说 python 没有注解,python 的注解是用来标注函数参数和返回值类型的,供 ide 做静态分析用,可选。
    freakxx
        18
    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
        19
    rizon  
    OP
       2018-12-03 21:36:51 +08:00
    @neoblackcap #14
    @wwqgtxx #7
    这事我终于整明白了,带参数的修饰器和不带参数的修饰器是不一样的。。
    带参数修饰器会在初始化时就执行修饰器的代码并将方法体重新赋值给方法名。
    不带参数修饰器在初始化的时候会将方法名赋值给修饰器方法,修饰器方法内部来手动调用被修饰的方法。
    wwqgtxx
        20
    wwqgtxx  
       2018-12-03 21:39:00 +08:00 via iPhone
    @rizon 你要理解 python 中函数是一类对象,一个函数是可以返回另一个函数作为返回值的,所以后面的(hello)并不是一个声明,而是一个链式函数调用,即 app.route('/')返回了一个函数,而后面的(hello)是用 hello 作为参数传递给个该函数,最后结果是再返回一个函数赋值给 hello,当然在 app.route 的这个地方,最后 hello 是没变的,但是实际上也可以完全不返回原函数,而返回另一个
    freakxx
        21
    freakxx  
       2018-12-03 21:42:01 +08:00
    @rizon
    本质上还是一样的,多一层少一层其实都是一样的。
    你反而可以把它当成是闭包来看待。

    所以修饰器可以无限嵌套下去,也可以在修饰器内部实现参数多层处理。
    只要最后返回函数本身即可。
    wwqgtxx
        22
    wwqgtxx  
       2018-12-03 21:42:11 +08:00
    @rizon 刚看到你 19#的回复,再说一下,传递的永远是个 function object 而不是什么“方法名”,只不过“带参数修饰器”本身需要返回一个函数,这个函数接受一个 callable 对象,而“不带参数修饰器”直接就是上面说的那个函数
    rizon
        23
    rizon  
    OP
       2018-12-03 21:53:46 +08:00
    @wwqgtxx #22 嗯嗯。是的,是这样的。谢谢。
    xpresslink
        24
    xpresslink  
       2018-12-03 22:00:07 +08:00
    @符号在 java 中是注解功能并不是实质功能,但是在 python 中 @后面跟的是装饰器,其实就是一个高阶函数,对下面的函数装饰,过程是把下面的函数当参数处理一下再输出一个处理过的函数。

    python 程序的执行过程是先把源码编译成字节码,然后再用解释器解释执行字节码。
    python 装饰器不是执行时调用的,而是在编译字节码的过程中就调用处理了,实际上相当于语法糖。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1884 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 52ms · UTC 16:32 · PVG 00:32 · LAX 08:32 · JFK 11:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.