美文网首页
Python的import机制

Python的import机制

作者: catHeart | 来源:发表于2019-01-06 15:06 被阅读19次

    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

    相关文章

      网友评论

          本文标题:Python的import机制

          本文链接:https://www.haomeiwen.com/subject/jtvxrqtx.html