Learning Python Part III 之 Import 是如何工作的

来源:互联网 发布:琉璃神社新的域名 编辑:程序博客网 时间:2024/06/05 07:24

一些人喜欢将Python 的模块import操作比作C的 #include,但是这是完全不同的——在Python中,import不仅仅是将一个文件中的代码插入到另一个文件中,而是真正的运行时操作符。通过完全不同的三步完成整个import操作:

  1. 找到模块文件
  2. 把它编译成字节代码(如果需要的话)
  3. 运行模块的代码建立其中定义的对象

但是值得注意的是,这三步只有在程序运行时一个模块第一次被import的时候才执行。之后在导入同一个模块的时候跳过这三步直接在内存中寻找已经加载的模块对象。确切的讲,Python通过把已经加载的模块储存到一个叫做sys.modules的表里,然后在执行import操作的开始在这个表里查找,如果没有就执行上面的三步。

查找

    首先,Python必须确定被import语句引用的文件的位置,注意到之前的例子中并没有路径,直接import b,而不是import c:\dir\b.py之类的,其中路径被有意的省略了。Python使用标准的模块搜索路径和已知的文件类型去定位相应的文件的位置。

编译(可能)

找到相应的代码文件之后,接下来如果必要的话Python会将文件编译成字节代码。在import的过程中Python会核对文件的修改时间和字节代码的Python版本号,然后决定如何处理。前者使用文件的时间戳,后者使用内嵌在字节代码中的数字或文件名(取决于Python的版本),这一步有下面两种选择:

编译

如果字节代码文件比源文件更老(例如源文件改变过),或者是由不同的Python版本创建,当程序运行的时候Python自动重新生成字节代码文件。

但是,这个模型在Python3.2之后被更改了——字节代码文件被隔离在__pycache__的子目录中,为了在多个Python版本被安装的情况下避免冲突和重复编译,文件以Python的版本命名。这就避免了检查版本号的需要,但是时间戳仍然被用来检查源文件是否被改变。

不编译

另一方面,如果Python发现了一个 .pyc的字节代码文件并不比相应的.py文件更老或者是由同一版本的Python创建的,就会跳过编译过程。

另外,如果Python在搜索路径中只找到了字节代码文件而没有源文件,它会直接加载字节代码文件。这意味这你可以传送一个只有字节代码的程序从而避免发送源代码。换句话说,编译过程可以被跳过以便加快程序的启动速度。

注意到编译发生在文件被import的时候。鉴于此,你并不会发现你的程序的顶层文件的.pyc字节代码,除非它在其他地方被导入了——只有被导入的文件你才会留下.pyc文件。顶层文件被设计用来直接执行,而不是被导入的。顶层文件的字节代码是在内部使用并在之后就废弃了;只有被导入的文件的字节代码被存储以便提高之后被导入的速度

运行

import操作的最后一步是运行模块文件的字节代码。文件中的所有语句按从上到下的顺序执行,在这一步中任何对变量的赋值都会产生对应的结果模块对象的属性,这就是模块中定义的工具如何建立的。例如,def语句在引用时会创建一个函数,然后将模块内的属性赋值给这些函数。这个函数可以在之后被文件的引入者调用。
因为import的最后一步实际上是运行的文件的代码,如果文件中的顶层代码(top-level)正常工作,你会在import时就看到结果。例如,模块中的顶层print语句在被导入的时候就会输出结果。而函数def语句定义对象以备之后调用.

原创粉丝点击