36.2. imp — 访问导入内部结构 — Python 文档

来自菜鸟教程
Python/docs/3.6/library/imp
跳转至:导航、​搜索

36.2. 小鬼 — 访问进口内件

源代码: :source:`Lib/imp.py`

自 3.4 版起已弃用:imp 包正在等待弃用,以支持 importlib



该模块为用于实现 import 语句的机制提供了一个接口。 它定义了以下常量和函数:

imp.get_magic()

返回用于识别字节编译代码文件(.pyc 文件)的魔法字符串值。 (对于每个 Python 版本,此值可能不同。)

自 3.4 版起已弃用: 改用 importlib.util.MAGIC_NUMBER

imp.get_suffixes()

返回 3 元素元组的列表,每个元组描述特定类型的模块。 每个三元组具有 (suffix, mode, type) 的形式,其中 suffix 是附加到模块名称以形成要搜索的文件名的字符串,mode 是要搜索的模式字符串传递给内置的 open() 函数来打开文件(对于文本文件可以是 'r',对于二进制文件可以是 'rb'),以及 type 是文件类型,其值为 PY_SOURCEPY_COMPILEDC_EXTENSION 之一,如下所述。

自 3.3 版起已弃用: 改用 importlib.machinery 上定义的常量。

imp.find_module(name[, path])

尝试找到模块 name。 如果省略 pathNone,则搜索由 sys.path 给出的目录名称列表,但首先搜索几个特殊的地方:该函数试图找到一个内置的-in 具有给定名称的模块 (C_BUILTIN),然后是一个冻结模块 (PY_FROZEN),在某些系统上,其他一些地方也会被查看(在 Windows 上,它会在可能指向特定文件的注册表)。

否则,path 必须是目录名列表; 搜索每个目录以查找具有上述 get_suffixes() 返回的任何后缀的文件。 列表中的无效名称将被静默忽略(但所有列表项都必须是字符串)。

如果搜索成功,则返回值为 3 元素元组 (file, pathname, description)

file 是一个打开的 file object 位于开头,pathname 是找到的文件的路径名,description 是一个 3- get_suffixes() 返回的列表中包含的元素元组,描述找到的模块类型。

如果模块不存在于文件中,则返回的 fileNonepathname 为空字符串,description 元组包含其后缀和模式为空字符串; 模块类型如上面括号中所示。 如果搜索不成功,则会引发 ImportError。 其他异常表明参数或环境存在问题。

如果模块是包,fileNonepathname 是包路径,description 元组中的最后一项是 ]PKG_DIRECTORY

此函数不处理分层模块名称(包含点的名称)。 为了找到PM,即包P的子模块M,使用find_module()load_module() 查找并加载包 P,然后使用 find_module() 并将 path 参数设置为 P.__path__。 当 P 本身具有带点名称时,递归应用此配方。

自 3.3 版起已弃用: 使用 importlib.util.find_spec() 代替,除非需要 Python 3.3 兼容性,在这种情况下使用 importlib.find_loader()。 例如前一种情况的用法,请参阅 importlib 文档的 Examples 部分。

imp.load_module(name, file, pathname, description)

加载先前由 find_module() 找到的模块(或通过其他方式进行的搜索产生兼容结果)。 这个函数不仅仅是导入模块:如果模块已经导入,它会重新加载模块! name 参数表示完整的模块名称(包括包名称,如果这是包的子模块)。 file参数是打开的文件,pathname是对应的文件名; 当模块是包或未从文件加载时,这些可以分别是 Nonedescription 参数是一个元组,由 get_suffixes() 返回,描述必须加载什么样的模块。

如果加载成功,则返回值为模块对象; 否则,会引发异常(通常为 ImportError)。

重要: 调用者负责关闭 file 参数,如果它不是 None,即使引发异常。 最好使用 try ... finally 语句来完成。

Deprecated since version 3.3: 如果以前与 imp.find_module() 结合使用,则考虑使用 importlib.import_module(),否则使用由您为 imp.find_module() 选择的替代品。 如果您直接使用文件路径参数调用 imp.load_module() 和相关函数,则使用 importlib.util.spec_from_file_location()importlib.util.module_from_spec( )。 有关各种方法的详细信息,请参阅 importlib 文档的 Examples 部分。

imp.new_module(name)

返回一个名为 name 的新空模块对象。 这个对象是而不是插入到sys.modules中。

自 3.4 版起已弃用: 改用 importlib.util.module_from_spec()

imp.reload(module)

重新加载之前导入的 模块 。 参数必须是一个模块对象,所以它之前必须成功导入。 如果您使用外部编辑器编辑了模块源文件并希望在不离开 Python 解释器的情况下试用新版本,这将非常有用。 返回值是模块对象(与 module 参数相同)。

当执行 reload(module) 时:

  • 重新编译 Python 模块的代码并重新执行模块级代码,定义一组新的对象,这些对象绑定到模块字典中的名称。 扩展模块的 init 函数不会被第二次调用。

  • 与 Python 中的所有其他对象一样,旧对象仅在其引用计数降至零后才会被回收。

  • 模块命名空间中的名称更新为指向任何新的或更改的对象。

  • 对旧对象的其他引用(例如模块外部的名称)不会重新引用到新对象,并且如果需要,必须在它们出现的每个命名空间中更新。

还有一些其他注意事项:

重新加载模块时,会保留其字典(包含模块的全局变量)。 名称的重新定义将覆盖旧定义,因此这通常不是问题。 如果模块的新版本未定义由旧版本定义的名称,则旧定义保留。 如果模块维护全局表或对象缓存,则此功能可用于模块的优势 - 通过 try 语句,它可以测试表的存在并在需要时跳过其初始化:

try:
    cache
except NameError:
    cache = {}

除了 sys__main__builtins 之外,重新加载内置或动态加载的模块通常不是很有用。 然而,在许多情况下,扩展模块不会被设计为多次初始化,并且在重新加载时可能会以任意方式失败。

如果一个模块使用 from ... import ... 从另一个模块导入对象,则为另一个模块调用 reload() 不会重新定义从它导入的对象——一个解决这个问题的方法是重新执行 from 语句,另一种是使用 import 和限定名称 (module.*name*) 代替。

如果模块实例化类的实例,重新加载定义类的模块不会影响实例的方法定义——它们继续使用旧的类定义。 对于派生类也是如此。

3.3 版更改: 依赖于 __name____loader__ 在重新加载的模块上定义,而不仅仅是 __name__

自 3.4 版起已弃用: 改用 importlib.reload()

以下函数是处理 PEP 3147 字节编译文件路径的便利。

3.2 版中的新功能。


imp.cache_from_source(path, debug_override=None)

PEP 3147 路径返回到与源 路径 关联的字节编译文件。 例如,如果 path/foo/bar/baz.py,则 Python 3.2 的返回值将是 /foo/bar/__pycache__/baz.cpython-32.pyccpython-32 字符串来自当前的魔法标签(见 get_tag();如果 sys.implementation.cache_tag 未定义,则 NotImplementedError 将被引发)。 通过为 debug_override 传入 TrueFalse,您可以覆盖 __debug__ 的系统值,从而优化字节码。

path 不需要存在。

3.3 版更改: 如果 sys.implementation.cache_tagNone,则引发 NotImplementedError

自 3.4 版起已弃用: 改用 importlib.util.cache_from_source()

3.5 版更改: debug_override 参数不再创建 .pyo 文件。

imp.source_from_cache(path)

给定 pathPEP 3147 文件名,返回关联的源代码文件路径。 例如,如果 path/foo/bar/__pycache__/baz.cpython-32.pyc,则返回的路径将是 /foo/bar/baz.pypath 不需要存在,但是如果它不符合 PEP 3147 格式,则会引发 ValueError。 如果未定义 sys.implementation.cache_tag,则会引发 NotImplementedError

3.3 版更改: 未定义 sys.implementation.cache_tag 时引发 NotImplementedError

自 3.4 版起已弃用: 改用 importlib.util.source_from_cache()

imp.get_tag()

返回 PEP 3147 与此版本 Python 幻数匹配的幻标记字符串,由 get_magic() 返回。

自 3.4 版起已弃用: 在 Python 3.3 中直接使用 sys.implementation.cache_tag

以下函数有助于与导入系统的内部锁定机制进行交互。 导入的锁定语义是一个实现细节,可能因版本而异。 然而,Python 确保循环导入工作没有任何死锁。

imp.lock_held()

如果当前持有全局导入锁,则返回 True,否则返回 False。 在没有线程的平台上,总是返回 False

在具有线程的平台上,执行导入的线程首先持有全局导入锁,然后为导入的其余部分设置每个模块的锁。 这会阻止其他线程导入同一模块,直到原始导入完成,从而防止其他线程看到由原始线程构造的不完整模块对象。 循环导入是一个例外,它通过构造必须在某个时候暴露一个不完整的模块对象。

3.3 版更改: 大部分锁定方案已更改为按模块锁定。 为一些关键任务保留全局导入锁,例如初始化每个模块的锁。

自 3.4 版起已弃用。

imp.acquire_lock()

为当前线程获取解释器的全局导入锁。 导入钩子应该使用这个锁来确保导入模块时的线程安全。

一旦一个线程获得了导入锁,同一个线程就可以再次获得它而不会阻塞; 线程每次获得它时都必须释放一次。

在没有线程的平台上,这个函数什么都不做。

3.3 版更改: 大部分锁定方案已更改为按模块锁定。 为一些关键任务保留全局导入锁,例如初始化每个模块的锁。

自 3.4 版起已弃用。

imp.release_lock()

释放解释器的全局导入锁。 在没有线程的平台上,这个函数什么都不做。

3.3 版更改: 大部分锁定方案已更改为按模块锁定。 为一些关键任务保留全局导入锁,例如初始化每个模块的锁。

自 3.4 版起已弃用。

本模块中定义的以下整数常量用于指示find_module()的搜索结果。

imp.PY_SOURCE

该模块是作为源文件找到的。

自 3.3 版起已弃用。

imp.PY_COMPILED

该模块被发现为已编译的代码目标文件。

自 3.3 版起已弃用。

imp.C_EXTENSION

该模块被发现为可动态加载的共享库。

自 3.3 版起已弃用。

imp.PKG_DIRECTORY

该模块是作为包目录找到的。

自 3.3 版起已弃用。

imp.C_BUILTIN

该模块被发现为内置模块。

自 3.3 版起已弃用。

imp.PY_FROZEN

该模块被发现为冻结模块。

自 3.3 版起已弃用。

class imp.NullImporter(path_string)

NullImporter 类型是一个 PEP 302 导入钩子,它通过找不到任何模块来处理非目录路径字符串。 使用现有目录或空字符串调用此类型会引发 ImportError。 否则,返回 NullImporter 实例。

实例只有一种方法:

find_module(fullname[, path])

该方法总是返回None,表示找不到请求的模块。

3.3 版更改: None 插入 sys.path_importer_cache 而不是 NullImporter 的实例。

自 3.4 版起已弃用:None 插入 sys.path_importer_cache

36.2.1. 例子

以下函数模拟 Python 1.4 之前的标准导入语句(无分层模块名称)。 (这个 implementation 在那个版本中不起作用,因为 find_module() 已经扩展并且 load_module() 已经在 1.4 中添加。)

import imp
import sys

def __import__(name, globals=None, locals=None, fromlist=None):
    # Fast path: see if the module has already been imported.
    try:
        return sys.modules[name]
    except KeyError:
        pass

    # If any of the following calls raises an exception,
    # there's a problem we can't handle -- let the caller handle it.

    fp, pathname, description = imp.find_module(name)

    try:
        return imp.load_module(name, fp, pathname, description)
    finally:
        # Since we may exit via an exception, close fp explicitly.
        if fp:
            fp.close()