python的from-import机制
在使用Python的过程中,我遇到了这么一个有关模块导入的bug。 先说我的目录结构
core
foo.py
bar.py
shit.py
fuck.py
__init__.py
(对,这就是我的目录结构😂) 其中fuck.py里import了shit和bar,shit和bar里都import了foo,但是import的方式是不一样的,所有文件内容如下:
# fuck.py
import shit
import bar
#...
---
# shit.py
from foo import set_flag
set_flag(True)
# ...
---
# bar.py
from core import foo
#...
foo.get_flag(True)
#...
---
# foo.py
if 'flag' not in globals():
globals()['flag'] = False
def get_flag():
# lock
flag = globals()['flag']
# unlock
return flag
def set_flag(cur_flag):
#lock
globals()['flag'] = cur_flag
#unlock
foo里边儿放了一个全局变量flag(同时为它配了个全局锁),然后配有get_flag和set_flag函数,然后我在bar.py里调用了get_flag,在shit里调用了set_flag,但是我发现,我在shit.py里set的值,在bar.py里get的时候并没有得到体现,然后我就debug跟了一下,发现在shit.py里set的那个flag,跟bar里get的flag,好像并不是同一个东西啊。。。好像有两个独立的flag一样。
去网上找了很多,发现是我没有对python的import机制了解清楚,这里涉及到“relative imports", “Absolute imports"的概念。
当一个py文件被加载时,它会有一个名字,如果py作为top-level脚本,则名字就是
__main__;如果作为module,它的名字是它所在的package或者subpackage, 和文件名用.连接起来形成的那个名字。
然而,一个module的名字,并没有上面说的那么简单。一个module的名字,取决于它是怎么被import进来的。
import一个module有这么几种方式:
import siblingimport mypkg.siblingfrom mypkg import siblingfrom . import sibling第一种和第四种都是relavite imports,第四种叫explicit relative imports.
第一种写法,导入标准库一般都这么写;导入非标准库,极其不推荐这么写。 第四种写法,有两个优点决定它是最好的写法,有一个缺点导致目前还是不要这么写为好😂,真是矛盾呢。两个优点是,如果module发生重命名或者移动,这种写法完全不需要修改代码;缺点是,如果这个py文件以
__main__执行的话,这么写就会import失败。
第二种和第三种都是package import方式,即absolute imports.这种方式目前是最推荐的写法,所有非标准库的module,都应该用这种方式去import。
用第一种写法impo非标准库module,如果成功,它的名字就是它本身。而第三种或第四种写法,如果import成功,它的名字才是上面所说的,它所在的package或者subpackage, 和文件名用
.连接起来形成的那个名字。
解释前面 bug,为什么看起来会有”两个独立的flag”呢?其实就是在bar和shit这两个文件import foo这个module的时候,用了两种不同的方式,一个是from core import foo,一个是from foo import set_flag, 这导致在bar.py里,foo这个module的名字其实是core.foo,而在shit.py里,foo的名字就是foo, 所以就会有两个flag。
one more thing
- 一般每个py文件开头都加上
from __future__ import absolute_import
以防止上述的第一种import方式,即隐式相对导入,强制必须使用absolute import。
-
可以在每个module里加上
__all__这个字段,当这个module被import出去时,可以用__all__来控制这个module里的哪些符号可以被import出去。
参考资料
- 原文作者:比克王国
- 原文链接:https://wgqimut.github.io/posts/python%E7%9A%84from-import%E6%9C%BA%E5%88%B6/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。