Python的import机制

2019-01-06  本文已影响19人  catHeart

Python程序中,import 语句导入一个新的模块供当前程序使用。import 的基本语法是 import module_name; 执行 import 语句时,Python解释器首先搜索到 module_name 指向的源码文件,然后加载到内存,并将新导入的模块绑定到一个变量上;这样就可以在后续操作中,通过这个绑定的变量访问新导入的模块了。

sys.path

import module_name时,Python 按照 sys.path 指定的文件夹列表依次进行搜索。sys.path是一个列表类型,根据操作系统和编译参数的不同,默认存储不同的文件夹列表。另外,也可以在程序运行过程中,动态地向 sys.path 添加或删除内容,实现所需的加载模块的需求。
在当前 Arch Linux (kernel 4.19.21) 机器上,查看 Python 3.7 的 sys.path 如下:

$ python
Python 3.7.1 (default, Oct 22 2018, 10:41:28) 
[GCC 8.2.1 20180831] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/home/hong/.local/lib/python3.7/site-packages', '/usr/lib/python3.7/site-packages']

sys.path默认的第一个文件夹地址为空,指向当前的工作目录,即os.getcwd(). 因此,搜索模块时,Python首先搜索的是当前工作目录。成功加载的模块,被放到 sys.modules 字典中;如果一个模块已经加载成功,存在于 sys.modules 中,那么下次对该模块再执行 加载操作时,就不执行搜索操作了,而是直接从 sys.modules 直接返回。

$ mkdir /tmp/demo
$ touch /tmp/demo/__init__.py  #在/tmp文件夹下,创建 demo模块
$ python
>>> import os, sys
>>> import demo     # /tmp目录不在sys.path中,无法加载成功
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'demo'
>>> 'demo' in sys.modules
False
>>> os.chdir('/tmp/')  #改变当前工作目录到/tmp,加载成功
>>> import demo
>>> demo
<module 'demo' from '/tmp/demo/__init__.py'>
>>> demo.__path__
['/tmp/demo']
>>> 'demo' in sys.modules
True

当然,也可以通过将'/tmp'文件夹添加到 sys.path 文件夹列表,加载上述 demo模块。

$ python
>>> import sys
>>> sys.path.append('/tmp')
>>> import demo
>>> demo
['/tmp/demo']

PYTHONPATH

除了在运行过程中修改sys.path,我们还可以通过环境变量PYHTONPATH来指定模块搜索的路径。Python会将 PYTHONPATH 的内容放到 sys.path 中当前工作目录''和其他默认搜索路径之间。

$ export PYTHONPATH='/tmp'
$ python
>>> import sys
>>> sys.path
['', '/tmp', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/home/hong/.local/lib/python3.7/site-packages', '/usr/lib/python3.7/site-packages']

如果要指定多个搜索路径,将它们都放到 PYTHONPATH 中即可。不同搜索路径之间需要一个分隔符进行分隔,但是因为操作系统的习惯会导致分隔符在不同系统下的差异。

$ export PYTHONPATH='/tmp:/tmp/lib2'
$ python
>>> import sys
>>> sys.path
['', '/tmp', '/tmp/lib2', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/home/hong/.local/lib/python3.7/site-packages', '/usr/lib/python3.7/site-packages']

重新加载模块

如前所述,Python完成模块加载后,会将新的模块放到 sys.modules 中。如果重新加载该模块,则不同执行搜索操作,而是从 sys.modules 中直接返回。但是,有时修改了模块的源码,确实需要完全重新加载模块;这时候,只要先将保存在 sys.modules 中原模块删除,然后再执行 import 操作即可。

通过环境变量 PYTHONPATH,加载上述demo模块。

$ export PYTHONPATH='/tmp'
$ python
>>> import demo
>>> demo.message
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'demo' has no attribute 'message'

demo模块中不存在message变量,访问出错。修改demo模块,添加message变量。

$ echo "message='this is a message.'" > /tmp/demo/__init__.py

在Python环境重新加载demo模块。

>>> import sys
>>> sys.modules.pop('demo')
>>> import demo
>>> demo.message
'this is a message'

Reference

上一篇下一篇

猜你喜欢

热点阅读