[关闭]
@spiritnotes 2016-02-18T04:36:46.000000Z 字数 1818 阅读 1313

Python语言实践分析 -- import

Python

原理

在Python中重用代码的机制是导入模块(也即是import),对于该机制其内部的主要流程如下:

Created with Raphaël 2.1.2Start获取需要导入的模块名sys.modules中是否存在对应模块名End按照搜索路径查找模块名将模块编译为字节码sys.modules中以模块名为关键字添加一个新建模块对象依次执行模块文件的代码,将其生成的对象添加到模块对象中yesno
  1. 在系统内部的sys.modules中以模块名作为字符串进行查找,是否已经存在该模块,如果存在则直接返回;
  2. 当不存在时,则按照相应的文件搜索规则找到对应的py文件(其实还包括扩展等),编译为字节码;
  3. 在sys.modules中以模块名字符串为key添加一个新建模块对象,然后依序执行该模块文件中的代码,依次将生成的对象添加到模块对象中;

实践

测试1 导入流程

为了验证如上流程,我们设计如下代码作为模块进行导入,被用于导入的文件为tobeimported.py,代码如下:

  1. import sys
  2. print('"tobeimported" in sys.modules -->',"tobeimported" in sys.modules)
  3. #被其他文件导入将输出 True,而单独执行本文件则为 False
  4. #import tobeimported 测试2,3中添加
  5. base = set(dir(sys.modules["tobeimported"]))
  6. x = 1
  7. print(set(dir(sys.modules["tobeimported"]))-base)
  8. #执行代码时刻增加对象,执行结果应该为刚刚创建的 base 和 x

针对如上测试代码,如果在其他文件中导入该模块文件,则结果如下:

  1. "tobeimported" in sys.modules --> True #说明最先是赋值sys.modules['tobeimported']
  2. {'x', 'base'} #说明模块对象可变,代码执行时改变模块对象

测试2 递归import

如果在测试1的代码中添加如下代码(随处)使其被导入,可见其对结果没有任何变化。

  1. import tobeimported

测试3 主动执行

如果单独执行被导入文件,则结果如下:

  1. "tobeimported" in sys.modules --> False
  2. "tobeimported" in sys.modules --> True
  3. {'x', 'base'}
  4. set()

可见tobeimported.py中代码被执行了2次。这里的原因是由于执行当前文件,sys.modules仍然会添加一个模块,其模块键值为 "__main__",因此当执行“import tobeimported”语句时,会重新执行一下本文件。

测试4 __name__替代

在测试3的基础上,将代码中的 "tobeimported" 替换为 __name__,则两次结果均为 base 和 x,如下:

  1. print('begin in module -->',__name__)
  2. import sys
  3. print(__name__,' in sys.modules -->',__name__ in sys.modules)
  4. import tobeimported
  5. base = set(dir(sys.modules[__name__]))
  6. x = 1
  7. print(set(dir(sys.modules[__name__]))-base)
  8. print('end in module -->',__name__)

执行结果:

  1. begin in module --> __main__
  2. __main__ in sys.modules --> True
  3. begin in module --> tobeimported
  4. tobeimported in sys.modules --> True
  5. {'base', 'x'}
  6. end in module --> tobeimported
  7. {'base', 'x'}
  8. end in module --> __main__

循环依赖

通过上面的分析,看下面的代码:

  1. # a.py
  2. import b
  3. def f():
  4. return b.x
  5. f()
  6. # b.py
  7. import a
  8. x = 1
  9. def g():
  10. return a.f()

如果我们直接导入a,可以发现一切正常,虽然b中也引用了a.f,但其位于函数内,导入的时候并不会执行;而导入b,则会由于b中执行import a时会调用f()导致其会引用到b.x,而此时b.x还没有定义,出现异常。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注