imp --- 访问 import 内部对象

源代码: Lib/imp.py

3.4 版後已棄用: imp 模块已被废弃,请改用 importlib


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

imp.get_magic()

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

3.4 版後已棄用: 改用 importlib.util.MAGIC_NUMBER

imp.get_suffixes()

返回一个 3 元素元组的列表,每个元组描述了一个特定的模块类型。 每个三元组的形式为 (suffix, mode, type),其中 suffix 是要附加到模块名称上以组成要搜索的文件名的字符串,mode 是要传给内置 open() 函数以打开该文件的模式字符串 (可以为针对文本文件的 'r' 或针对二进制文件的 'rb'),而 type 是文件类型,它的值为 PY_SOURCE, PY_COMPILED 之一 C_EXTENSION,具体说明如下。

3.3 版後已棄用: 改用在 importlib.machinery 上定义的常量。

imp.find_module(name[, path])

尝试找到模块 name。 如果 path 被省略或为 None,则会搜索 sys.path 给出的目录名列表,但在此前会先搜索几个特殊位置:该函数将尝试找到具有给定名称的内置模块 (C_BUILTIN),然后是冻结模块 (PY_FROZEN),并且在某些系统上还会查找其他几个位置(在 Windows 上,它将查看注册表,其中可能指向某一特定文件)。

在其他情况下,path 必须是一个目录名列表;将在每个目录中搜索具有上述 get_suffixes() 所返回的任何后缀的文件。 列表中的无效名称将被静默地忽略(但是所有列表条目都必须为字符串)。

如果搜索成功,返回值将是一个 3 元素元组 (file, pathname, description):

file 是一个放在最前面的已打开的 file objectpathname 是打到的文件的路径名,而 description 是一个描述所找到的模块种类的 3 元素元组,如 get_suffixes() 所返回的列表中包含的内容。

如果模块为内置或冻结模块则 filepathname 都将为 None 并且 description 元组将包含空字符串来表示其后缀和模式;模块类型按上述圆括号中所给出的内容来指明。 如果搜索未成功,则会引发 ImportError。 其他异常被用于指示参数或环境问题。

如果模块是一个包,则 file 将为 Nonepathname 将为包路径而 description 元组的最后一项将为 PKG_DIRECTORY

此函数不会处理多层级的模块名称(包含点号的名称)。 为了找到 P.M,也就是 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 文档的 例子 小节。

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

加载之前由 find_module() 所找到的模块(或由其他方式执行搜索所产生的兼容的结果)。 此函数所做的不只是导入模块:如果模块已经被导入,它将重新加载此模块! name 参数指明完整的模块名称(包括包名,如果它是一个包的子模块的话)。 file 参数是一个打开的文件,而 pathname 是相应的文件名;当模块是一个包或者不是从文档加载时,它们可以分别为 None''description 参数是一个元组,如 get_suffixes() 将返回的内容一样,描述了必须加载什么样的模块。

如果加载成功,则返回值为相应的模块对象;否则,将引发一个异常 (通常为 ImportError)。

重要: 调用方需负责关闭 file 参数,如果它不为 None 的话,即使是在有异常被引发时。 这最好是使用 try ... finally 语句来实现。

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 文档的 例子 小节来了解各种方式的细节。

imp.new_module(name)

返回一个新的名为 name 的空模块对象。 此对象 不会 被插入到 sys.modules 中。

3.4 版後已棄用: 请改用 importlib.util.module_from_spec()

imp.reload(module)

重新加载之前导入的 module。 该参数必须是一个模块对象,因此它之前必须已经被成功导入了。 这在当你已使用外部编辑器编辑过模块源代码文件并想在不退出 Python 解释器的情况下尝试新版本时会很有用处。 返回值为模块对象(与 module 参数所指向的相同)。

reload(module) 被执行时:

  • Python 模块的代码会被重新编译并且模块层级的代码将重新执行,定义一个新的绑定到模块字典中的名称的对象集合。 扩展模块的 init 函数不会被重复调用。

  • 与Python中的所有的其它对象一样,旧的对象只有在它们的引用计数为0之后才会被回收。

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

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

有一些其他注意事项:

当一个模块被重新加载的时候,它的字典(包含了那个模块的全区变量)会被保留。名称的重新定义会覆盖旧的定义,所以通常来说这不是问题。如果一个新模块没有定义在旧版本模块中定义的名称,则将保留旧版本中的定义。这一特性可用于作为那个模块的优点,如果它维护一个全局表或者对象的缓存 —— 使用 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 相关联的已编译字节码文件的路径。 举例来说,如果 path/foo/bar/baz.py 则 Python 3.2 中的返回值将是 /foo/bar/__pycache__/baz.cpython-32.pyc。 字符串 cpython-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)

根据给定的 PEP 3147 文件名的 path,返回相关联的源代码文件路径。 举例来说,如果 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()

返回与此 Python 版本魔数相匹配的 PEP 3147 魔术标签字符串,如 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

例子

以下函数模拟了 Python 1.4 及之前版本的标准导入语句(没有多层级的模块名称)。 (这个 实现 不可用于那些版本,因为 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()