有一个类似的示例:
有两个文件app.py、test1.py.
app.py
test1.py
执行python app.py,”/test1”的url能注册进去吗,答案是不能。
为什么呢。
我们加上调试信息:
app.py
test1.py
同时修改app.route的源码:
调试信息如下:(add url)
分析调用过程:
- 首先进入app.py,初始化app,可以看到第一条语句”create app”,然后是注册app中url,然后导入test1,这个时候在app中是不包含app的module的,这是当然的,app.py里也没有import app。
- 进入test1,这个时候碰到第一句
from app import app
,按照 总结的module导入规则,就是在sys.modules里面去找这个模块,如果找不到,就实例化这个module模块的对象,然后从新的这个对象中,取出import进来的对象。这里可以看到,test1中判断是没有app的module的。因此,在start init test1
以后,继续进入了app.py文件,又输出了一个create app
!相当于是把app.py又执行了一次。可以看到后面的add url
又出现了一次,import test1
也是。但这里不会循环引用,因为app中也没有引用test1的内容,只是import
,发现已经import
过了(import会先添加到sys.module里面,然后再执行,可以看到导入app里面,已经出现了app get app
)就会跳过。这个时候算是完成了from app import app
这句话。
- 前面重点是,test1中取得的app,是test1中重新初始化一次的app!和app.py里的app不是一个。那之后的add url,也是加到这个新的app中。而在main里面,我们启动的是app.py里的py,因此,显然我们无法从浏览器访问到test1这个url。(但是如果在test1中,app.run,这个是能访问到所有url的)
怎么办呢?
从app.py中拆分一个run.py出来。
这是因为为什么不对,就是test1中重新实例化一个app,而为什么重新实例化一个app,是因为app不存在sys.module里面,因此只需要在调用test1时,app已经被人导入过就好办了。因此,应该将main拆分到一个单独的文件中。
可以看看新的调试信息
这个时候只有一个
create app
了,同时这次app里直接就输出了app get app .
启示:
这个示例有好几个重要的作用:
- 一是深入理解python的执行过程,以及module导入的本质概念。
- 不要导入作为main的包,会重复执行。作为替代,分解到单独的、简单的一个py中。
- 所有module都导入到sys.module中,重复被后续所使用。即一个包只有被运行一次。
- 根据上一点,这就能够借助monkey patch,先导入包的地方,对包进行动态变更,则后续再导入这个包的地方都会使用到变更后的函数。这就是动态语言的魔性。
扩展:
- 虽然用的是同一份包的内容,但要注意的是,导入到当前module时,会包含在当前module的命名空间中
- 因此如果使用monkey patch在这里直接修改导入后的对象是没用的,该对象仅作用于当前空间,需要使用导入包.对象去访问唯一的对象。
这里对于jwt_required这个对象,只有当前文件使用,修改这个对外界是没有影响的,除非其他地方import该文件的jwt_required,相反,后面对于flask_jwt_extended.jwt_required,是全局通用的。后续的所有地方去import flask_jwt_extended.jwt_required都会使用变化后的函数。
- 作者:Olimi
- 链接:https://olimi.icu/article/469e4f9e-1b5e-4f9f-851a-8749bf37c2db
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。