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的方式是不一样的,所有文件内容如下:

       
      
      
      
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 ():
# lock
flag = globals()[ 'flag']
# unlock
return flag
def set_flag(cur_flag):
#lock
globals()[ 'flag'] = cur_flag
#unlock

foo里边儿放了一个全局变量flag(同时为它配了个全局锁),然后配有get_flagset_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有这么几种方式:

  1. import sibling
  2. import mypkg.sibling
  3. from mypkg import sibling
  4. 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机制


你可能感兴趣的:(python的from-import机制)