在使用Python的过程中,我遇到了这么一个有关模块导入的bug。
先说我的目录结构
|
(对,这就是我的目录结构)
其中fuck.py里import了shit和bar,shit和bar里都import了foo,但是import的方式是不一样的,所有文件内容如下:
|
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 sibling
import mypkg.sibling
from mypkg import sibling
from . import sibling
第一种和第四种都是relavite imports,第四种叫explicit relative imports.
第一种写法,导入标准库一般都这么写;导入非标准库,极其不推荐这么写。
第四种写法,有两个优点决定它是最好的写法,有一个缺点导致目前还是不要这么写为好:joy:,真是矛盾呢。两个优点是,如果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出去。PEP8关于imports的编码规范
参考资料
- stackoverflow import问答
原文:大专栏 python的from-import机制