# 36.2. `imp` - 访问import内部

源代码： Lib/imp.py

自版本3.4后已弃用： `imp`包正在等待废弃，换成`importlib`。

此模块提供了用于实现`import`语句的机制的接口。它定义了以下常量和函数：

`imp.``get_magic`()

  返回用于识别字节编译代码文件（`.pyc`文件）的魔术字符串值。（每个Python版本的此值可能不同。）

  自版本3.4后已弃用：改用`importlib.util.MAGIC_NUMBER`。

`imp.``get_suffixes`()

  返回一个3元素元组的列表，每个元组描述特定类型的模块。每个三元组具有`（后缀， 模式， 类型）`的形式，其中是要附加到模块名称以形成要搜索的文件名的字符串，模式是要传递到内建`open()`函数的模式字符串，文件（对于文本文件可以是`'r'`，对于二进制文件可以是`'rb'`），类型是文件类型，以下描述的值`PY_SOURCE`，`PY_COMPILED`或`C_EXTENSION`中的一个。

  自版本3.3后已弃用：改用`importlib.machinery`中定义的常量。

`imp.``find_module`(name[, path])

  尝试找到名称。如果省略path或`None`，将搜索由`sys.path`给出的目录名称列表，但首先搜索几个特殊位置：该函数尝试查找具有给定名称（`C_BUILTIN`），冻结模块（`PY_FROZEN`）的内建模块，在某些系统上，一些其他地方被查找（在Windows上，它注册表中可能指向一个特定的文件）。

  否则，路径必须是目录名称列表；将搜索每个目录中具有由上述`get_suffixes()`返回的任何后缀的文件。列表中无效的名称将被忽略（但所有列表项必须是字符串）。

  如果搜索成功，返回值为3元素元组`（文件， 路径名， 描述）`

  文件是位于开头的开放file object，路径名是找到的文件的路径名，描述是由`get_suffixes()`返回的列表中包含的描述所找到的模块类型的3元素元组。

  如果模块不在文件中，返回的文件为`None`，路径名为空字符串，/ t4> tuple包含其后缀和模式的空字符串；模块类型如上面括号中所示。如果搜索不成功，则会引发`ImportError`。其他异常表示参数或环境的问题。

  如果模块是包，文件是`None`，路径名是包路径和描述 t4中的最后一个项目> tuple是`PKG_DIRECTORY`。

  此函数不处理分层模块名称（包含点的名称）。为了找到PM，即包裹P的子模块M，请使用`find_module()`和`load_module()`找到并加载包P，然后使用`find_module()`，将路径参数设置为`P.__path__`。当P本身有点名时，以递归方式应用此配方。

  自版本3.3后已弃用：除非需要Python 3.3兼容性，否则使用`importlib.util.find_spec()`，在这种情况下，请使用`importlib.find_loader()`

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

  加载以前由`find_module()`发现的模块（或通过其他方式进行的搜索，产生兼容的结果）。此函数不仅仅导入模块：如果模块已经导入，它将重新加载模块！name参数指示完整的模块名称（包括程序包名称，如果它是程序包的子模块）。文件参数是打开的文件，路径名是相应的文件名；当模块是包或者没有从文件加载时，这些可以分别是`None`和`''`。描述参数是一个元组，由`get_suffixes()`返回，描述必须加载什么样的模块。

  如果加载成功，返回值是模块对象；否则，引发异常（通常`ImportError`）。

  重要提示：如果不是`None`，即使发生异常，调用者也负责关闭文件参数。最好使用`try` ... `finally`语句。

  从版本3.3开始弃用：如果之前与`imp.find_module()`结合使用，则考虑使用`importlib.import_module()`，否则使用加载器由您为`imp.find_module()`选择的替换返回。如果您直接调用`imp.load_module()`和相关函数，请使用`importlib.machinery`中的类，例如 `importlib.machinery.SourceFileLoader（name， path）.load_module()`。

`imp.``new_module`(name)

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

  自版本3.4后已弃用：改用`types.ModuleType`。

`imp.``reload`(module)

  重新载入先前导入的模块。参数必须是一个模块对象，因此必须先被成功导入。如果您已使用外部编辑器编辑了模块源文件，并希望在不离开Python解释器的情况下尝试新版本，这将非常有用。返回值是模块对象（与模块参数相同）。

  当执行`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`，则返回值为`/foo/bar/__pycache__/baz.cpython-32.pyc` for Python 3.2。`cpython-32`字符串来自当前的魔术标签（参见`get_tag()`；如果没有定义`sys.implementation.cache_tag`，则`NotImplementedError`会被提出）。通过为debug_override传递`True`或`False`，您可以覆盖`__debug__`的系统值，从而导致优化的字节码。

  路径不需要存在。

  在版本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)

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

  在未定义`sys.implementation.cache_tag`时，在版本3.3中更改了提升`NotImplementedError`：

  自从版本3.4开始弃用：改为使用`importlib.util.source_from_cache()`。

`imp.``get_tag`()

  返回与`get_magic()`返回的Python的幻数的版本匹配的 PEP 3147魔术标记字符串。

  自版本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的标准import语句（没有分层模块名称）。（由于`find_module()`已经扩展，并且在1.4中添加了`load_module()`，因此此实现

    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()