imp — 访问导入内部结构 — Python 文档
imp — 访问 import 内部
源代码: :source:`Lib/imp.py`
该模块为用于实现 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_SOURCE、PY_COMPILED 或 C_EXTENSION 之一,如下所述。自 3.3 版起已弃用: 改用 importlib.machinery 上定义的常量。
- imp.find_module(name[, path])
尝试找到模块 name。 如果省略 path 或
None
,则搜索由sys.path
给出的目录名称列表,但首先搜索几个特殊的地方:该函数试图找到一个内置的-in 具有给定名称的模块 (C_BUILTIN),然后是一个冻结模块 (PY_FROZEN),在某些系统上,其他一些地方也会被查看(在 Windows 上,它会在可能指向特定文件的注册表)。否则,path 必须是目录名列表; 搜索每个目录以查找具有上述 get_suffixes() 返回的任何后缀的文件。 列表中的无效名称将被静默忽略(但所有列表项都必须是字符串)。
如果搜索成功,则返回值为 3 元素元组
(file, pathname, description)
:file 是一个打开的 file object 位于开头,pathname 是找到的文件的路径名,description 是一个 3- get_suffixes() 返回的列表中包含的元素元组,描述找到的模块类型。
如果模块是内置或冻结的,那么 file 和 pathname 都是
None
并且 description 元组包含其后缀的空字符串和模式; 模块类型如上面括号中所示。 如果搜索不成功,则会引发 ImportError。 其他异常表明参数或环境存在问题。如果模块是包,file 是
None
,pathname 是包路径,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是对应的文件名; 当模块是包或未从文件加载时,这些可以分别是
None
和。 description 参数是一个元组,由 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.pyc
。cpython-32
字符串来自当前的魔法标签(参见 get_tag();如果sys.implementation.cache_tag
未定义,则 NotImplementedError 将被引发)。 通过为 debug_override 传入True
或False
,您可以覆盖__debug__
的系统值,从而优化字节码。path 不需要存在。
3.3 版更改: 如果
sys.implementation.cache_tag
是None
,则引发 NotImplementedError。自 3.4 版起已弃用: 改用 importlib.util.cache_from_source()。
3.5 版更改: debug_override 参数不再创建
.pyo
文件。
- imp.source_from_cache(path)
给定 path 到 PEP 3147 文件名,返回关联的源代码文件路径。 例如,如果 path 是
/foo/bar/__pycache__/baz.cpython-32.pyc
,则返回的路径将是/foo/bar/baz.py
。 path 不需要存在,但是如果它不符合 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
。
例子
以下函数模拟 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()