Files
hustoj/web/python-docs/reference/import.html
2025-03-09 17:10:56 +08:00

749 lines
120 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_CN">
<head>
<meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>5. 导入系统 &#8212; Python 3.8.20 文档</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/language_data.js"></script>
<script src="../_static/translations.js"></script>
<script src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml"
title="在 Python 3.8.20 文档 中搜索"
href="../_static/opensearch.xml"/>
<link rel="author" title="关于这些文档" href="../about.html" />
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="copyright" title="版权所有" href="../copyright.html" />
<link rel="next" title="6. 表达式" href="expressions.html" />
<link rel="prev" title="4. 执行模型" href="executionmodel.html" />
<link rel="canonical" href="https://docs.python.org/3/reference/import.html" />
<style>
@media only screen {
table.full-width-table {
width: 100%;
}
}
</style>
<link rel="shortcut icon" type="image/png" href="../_static/py.svg" />
<script type="text/javascript" src="../_static/copybutton.js"></script>
<script type="text/javascript" src="../_static/menu.js"></script>
</head>
<body>
<div class="mobile-nav">
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu" />
<label for="menuToggler" class="toggler__label">
<span></span>
</label>
<nav class="nav-content" role="navigation">
<a href="https://www.python.org/" class="nav-logo">
<img src="../_static/py.svg" alt="Logo"/>
</a>
<div class="version_switcher_placeholder"></div>
<form role="search" class="search" action="../search.html" method="get">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
<path fill-rule="nonzero"
d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="#444"></path>
</svg>
<input type="text" name="q" aria-label="快速搜索"/>
<input type="submit" value="转向"/>
</form>
</nav>
<div class="menu-wrapper">
<nav class="menu" role="navigation" aria-label="main navigation">
<div class="language_switcher_placeholder"></div>
<h3><a href="../contents.html">目录</a></h3>
<ul>
<li><a class="reference internal" href="#">5. 导入系统</a><ul>
<li><a class="reference internal" href="#importlib">5.1. <code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib</span></code></a></li>
<li><a class="reference internal" href="#packages">5.2. 包</a><ul>
<li><a class="reference internal" href="#regular-packages">5.2.1. 常规包</a></li>
<li><a class="reference internal" href="#namespace-packages">5.2.2. 命名空间包</a></li>
</ul>
</li>
<li><a class="reference internal" href="#searching">5.3. 搜索</a><ul>
<li><a class="reference internal" href="#the-module-cache">5.3.1. 模块缓存</a></li>
<li><a class="reference internal" href="#finders-and-loaders">5.3.2. 查找器和加载器</a></li>
<li><a class="reference internal" href="#import-hooks">5.3.3. 导入钩子</a></li>
<li><a class="reference internal" href="#the-meta-path">5.3.4. 元路径</a></li>
</ul>
</li>
<li><a class="reference internal" href="#loading">5.4. 加载</a><ul>
<li><a class="reference internal" href="#loaders">5.4.1. 加载器</a></li>
<li><a class="reference internal" href="#submodules">5.4.2. 子模块</a></li>
<li><a class="reference internal" href="#module-spec">5.4.3. 模块规格说明</a></li>
<li><a class="reference internal" href="#import-related-module-attributes">5.4.4. 导入相关的模块属性</a></li>
<li><a class="reference internal" href="#module-path">5.4.5. module.__path__</a></li>
<li><a class="reference internal" href="#module-reprs">5.4.6. 模块的 repr</a></li>
<li><a class="reference internal" href="#cached-bytecode-invalidation">5.4.7. 已缓存字节码的失效</a></li>
</ul>
</li>
<li><a class="reference internal" href="#the-path-based-finder">5.5. 基于路径的查找器</a><ul>
<li><a class="reference internal" href="#path-entry-finders">5.5.1. 路径条目查找器</a></li>
<li><a class="reference internal" href="#path-entry-finder-protocol">5.5.2. 路径条目查找器协议</a></li>
</ul>
</li>
<li><a class="reference internal" href="#replacing-the-standard-import-system">5.6. 替换标准导入系统</a></li>
<li><a class="reference internal" href="#package-relative-imports">5.7. 包相对导入</a></li>
<li><a class="reference internal" href="#special-considerations-for-main">5.8. 有关 __main__ 的特殊事项</a><ul>
<li><a class="reference internal" href="#main-spec">5.8.1. __main__.__spec__</a></li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">5.9. 开放问题项</a></li>
<li><a class="reference internal" href="#references">5.10. 参考文献</a></li>
</ul>
</li>
</ul>
<h4>上一个主题</h4>
<p class="topless"><a href="executionmodel.html"
title="上一章"><span class="section-number">4. </span>执行模型</a></p>
<h4>下一个主题</h4>
<p class="topless"><a href="expressions.html"
title="下一章"><span class="section-number">6. </span>表达式</a></p>
<div role="note" aria-label="source link">
<h3>本页</h3>
<ul class="this-page-menu">
<li><a href="../bugs.html">报告 Bug</a></li>
<li>
<a href="https://github.com/python/cpython/blob/3.8/Doc/reference/import.rst"
rel="nofollow">显示源代码
</a>
</li>
</ul>
</div>
</nav>
</div>
</div>
<div id="outdated-warning" style="padding: .5em; text-align: center; background-color: #FFBABA; color: #6A0E0E;">
这个文档所针对的是一个已不再受支持的 Python 旧版本。
你应当升级版本,并阅读
<a href="/3/reference/import.html"> Python 当前稳定版本的文档</a>.
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>导航</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="总目录"
accesskey="I">索引</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python 模块索引"
>模块</a> |</li>
<li class="right" >
<a href="expressions.html" title="6. 表达式"
accesskey="N">下一页</a> |</li>
<li class="right" >
<a href="executionmodel.html" title="4. 执行模型"
accesskey="P">上一页</a> |</li>
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> &#187;</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.8.20 Documentation</a> &#187;
</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python语言参考</a> &#187;</li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
<input type="submit" value="转向" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
|
</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="the-import-system">
<span id="importsystem"></span><h1><span class="section-number">5. </span>导入系统<a class="headerlink" href="#the-import-system" title="永久链接至标题"></a></h1>
<p id="index-0">一个 <a class="reference internal" href="../glossary.html#term-module"><span class="xref std std-term">module</span></a> 内的 Python 代码通过 <a class="reference internal" href="../glossary.html#term-importing"><span class="xref std std-term">importing</span></a> 操作就能够访问另一个模块内的代码。 <a class="reference internal" href="simple_stmts.html#import"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code></a> 语句是发起调用导入机制的最常用方式,但不是唯一的方式。 <a class="reference internal" href="../library/importlib.html#importlib.import_module" title="importlib.import_module"><code class="xref py py-func docutils literal notranslate"><span class="pre">importlib.import_module()</span></code></a> 以及内置的 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 等函数也可以被用来发起调用导入机制。</p>
<p><a class="reference internal" href="simple_stmts.html#import"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code></a> 语句结合了两个操作;它先搜索指定名称的模块,然后将搜索结果绑定到当前作用域中的名称。 <code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code> 语句的搜索操作被定义为对 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 函数的调用并带有适当的参数。 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 的返回值会被用于执行 <code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code> 语句的名称绑定操作。 请参阅 <code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code> 语句了解名称绑定操作的更多细节。</p>
<p><a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 的直接调用将仅执行模块搜索以及在找到时的模块创建操作。 不过也可能产生某些副作用,例如导入父包和更新各种缓存 (包括 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a>),只有 <a class="reference internal" href="simple_stmts.html#import"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code></a> 语句会执行名称绑定操作。</p>
<p><a class="reference internal" href="simple_stmts.html#import"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code></a> 语句被执行时,标准的内置 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 函数会被调用。 其他发起调用导入系统的机制 (例如 <a class="reference internal" href="../library/importlib.html#importlib.import_module" title="importlib.import_module"><code class="xref py py-func docutils literal notranslate"><span class="pre">importlib.import_module()</span></code></a>) 可能会选择绕过 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 并使用它们自己的解决方案来实现导入机制。</p>
<p>当一个模块首次被导入时Python 会搜索该模块,如果找到就创建一个 module 对象 <a class="footnote-reference brackets" href="#fnmo" id="id1">1</a> 并初始化它。 如果指定名称的模块未找到,则会引发 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a>。 当发起调用导入机制时Python 会实现多种策略来搜索指定名称的模块。 这些策略可以通过使用使用下文所描述的多种钩子来加以修改和扩展。</p>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.3 版更改: </span>导入系统已被更新以完全实现 <span class="target" id="index-46"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0302"><strong>PEP 302</strong></a> 中的第二阶段要求。 不会再有任何隐式的导入机制 —— 整个导入系统都通过 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 暴露出来。 此外,对原生命名空间包的支持也已被实现 (参见 <span class="target" id="index-47"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a>)。</p>
</div>
<section id="importlib">
<h2><span class="section-number">5.1. </span><a class="reference internal" href="../library/importlib.html#module-importlib" title="importlib: The implementation of the import machinery."><code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib</span></code></a><a class="headerlink" href="#importlib" title="永久链接至标题"></a></h2>
<p><a class="reference internal" href="../library/importlib.html#module-importlib" title="importlib: The implementation of the import machinery."><code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib</span></code></a> 模块提供了一个丰富的 API 用来与导入系统进行交互。 例如 <a class="reference internal" href="../library/importlib.html#importlib.import_module" title="importlib.import_module"><code class="xref py py-func docutils literal notranslate"><span class="pre">importlib.import_module()</span></code></a> 提供了相比内置的 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 更推荐、更简单的 API 用来发起调用导入机制。 更多细节请参看 <a class="reference internal" href="../library/importlib.html#module-importlib" title="importlib: The implementation of the import machinery."><code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib</span></code></a> 库文档。</p>
</section>
<section id="packages">
<h2><span class="section-number">5.2. </span><a class="headerlink" href="#packages" title="永久链接至标题"></a></h2>
<p id="index-3">Python 只有一种模块对象类型,所有模块都属于该类型,无论模块是用 Python、C 还是别的语言实现。 为了帮助组织模块并提供名称层次结构Python 还引入了 <a class="reference internal" href="../glossary.html#term-package"><span class="xref std std-term"></span></a> 的概念。</p>
<p>你可以把包看成是文件系统中的目录,并把模块看成是目录中的文件,但请不要对这个类比做过于字面的理解,因为包和模块不是必须来自于文件系统。 为了方便理解本文档,我们将继续使用这种目录和文件的类比。 与文件系统一样,包通过层次结构进行组织,在包之内除了一般的模块,还可以有子包。</p>
<p>要注意的一个重点概念是所有包都是模块,但并非所有模块都是包。 或者换句话说,包只是一种特殊的模块。 特别地,任何具有 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性的模块都会被当作是包。</p>
<p>所有模块都有自己的名字。 子包名与其父包名以点号分隔,与 Python 的标准属性访问语法一致。 例如你可能看到一个名为 <a class="reference internal" href="../library/sys.html#module-sys" title="sys: Access system-specific parameters and functions."><code class="xref py py-mod docutils literal notranslate"><span class="pre">sys</span></code></a> 的模块,以及一个名为 <a class="reference internal" href="../library/email.html#module-email" title="email: Package supporting the parsing, manipulating, and generating email messages."><code class="xref py py-mod docutils literal notranslate"><span class="pre">email</span></code></a> 的包,这个包又有一个名为 <a class="reference internal" href="../library/email.mime.html#module-email.mime" title="email.mime: Build MIME messages."><code class="xref py py-mod docutils literal notranslate"><span class="pre">email.mime</span></code></a> 的子包和该子包中的名为 <code class="xref py py-mod docutils literal notranslate"><span class="pre">email.mime.text</span></code> 的子包。</p>
<section id="regular-packages">
<h3><span class="section-number">5.2.1. </span>常规包<a class="headerlink" href="#regular-packages" title="永久链接至标题"></a></h3>
<p id="index-4">Python 定义了两种类型的包,<a class="reference internal" href="../glossary.html#term-regular-package"><span class="xref std std-term">常规包</span></a><a class="reference internal" href="../glossary.html#term-namespace-package"><span class="xref std std-term">命名空间包</span></a>。 常规包是传统的包类型,它们在 Python 3.2 及之前就已存在。 常规包通常以一个包含 <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> 文件的目录形式实现。 当一个常规包被导入时,这个 <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> 文件会隐式地被执行,它所定义的对象会被绑定到该包命名空间中的名称。<code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> 文件可以包含与任何其他模块中所包含的 Python 代码相似的代码Python 将在模块被导入时为其添加额外的属性。</p>
<p>例如,以下文件系统布局定义了一个最高层级的 <code class="docutils literal notranslate"><span class="pre">parent</span></code> 包和三个子包:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">parent</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">one</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">two</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">three</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
</pre></div>
</div>
<p>导入 <code class="docutils literal notranslate"><span class="pre">parent.one</span></code> 将隐式地执行 <code class="docutils literal notranslate"><span class="pre">parent/__init__.py</span></code><code class="docutils literal notranslate"><span class="pre">parent/one/__init__.py</span></code>。 后续导入 <code class="docutils literal notranslate"><span class="pre">parent.two</span></code><code class="docutils literal notranslate"><span class="pre">parent.three</span></code> 则将分别执行 <code class="docutils literal notranslate"><span class="pre">parent/two/__init__.py</span></code><code class="docutils literal notranslate"><span class="pre">parent/three/__init__.py</span></code></p>
</section>
<section id="namespace-packages">
<h3><span class="section-number">5.2.2. </span>命名空间包<a class="headerlink" href="#namespace-packages" title="永久链接至标题"></a></h3>
<p id="index-5">命名空间包是由多个 <a class="reference internal" href="../glossary.html#term-portion"><span class="xref std std-term">部分</span></a> 构成的,每个部分为父包增加一个子包。 各个部分可能处于文件系统的不同位置。 部分也可能处于 zip 文件中、网络上,或者 Python 在导入期间可以搜索的其他地方。 命名空间包并不一定会直接对应到文件系统中的对象;它们有可能是无实体表示的虚拟模块。</p>
<p>命名空间包的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性不使用普通的列表。 而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a>) 发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。</p>
<p>命名空间包没有 <code class="docutils literal notranslate"><span class="pre">parent/__init__.py</span></code> 文件。 实际上,在导入搜索期间可能找到多个 <code class="docutils literal notranslate"><span class="pre">parent</span></code> 目录,每个都由不同的部分所提供。 因此 <code class="docutils literal notranslate"><span class="pre">parent/one</span></code> 的物理位置不一定与 <code class="docutils literal notranslate"><span class="pre">parent/two</span></code> 相邻。 在这种情况下Python 将为顶级的 <code class="docutils literal notranslate"><span class="pre">parent</span></code> 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。</p>
<p>另请参阅 <span class="target" id="index-48"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a> 了解对命名空间包的规格描述。</p>
</section>
</section>
<section id="searching">
<h2><span class="section-number">5.3. </span>搜索<a class="headerlink" href="#searching" title="永久链接至标题"></a></h2>
<p>为了开始搜索Python 需要被导入模块(或者包,对于当前讨论来说两者没有差别)的完整 <a class="reference internal" href="../glossary.html#term-qualified-name"><span class="xref std std-term">限定名称</span></a>。 此名称可以来自 <a class="reference internal" href="simple_stmts.html#import"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">import</span></code></a> 语句所带的各种参数,或者来自传给 <a class="reference internal" href="../library/importlib.html#importlib.import_module" title="importlib.import_module"><code class="xref py py-func docutils literal notranslate"><span class="pre">importlib.import_module()</span></code></a><a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 函数的形参。</p>
<p>此名称会在导入搜索的各个阶段被使用,它也可以是指向一个子模块的带点号路径,例如 <code class="docutils literal notranslate"><span class="pre">foo.bar.baz</span></code>。 在这种情况下Python 会先尝试导入 <code class="docutils literal notranslate"><span class="pre">foo</span></code>,然后是 <code class="docutils literal notranslate"><span class="pre">foo.bar</span></code>,最后是 <code class="docutils literal notranslate"><span class="pre">foo.bar.baz</span></code>。 如果这些导入中的任何一个失败,都会引发 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a></p>
<section id="the-module-cache">
<h3><span class="section-number">5.3.1. </span>模块缓存<a class="headerlink" href="#the-module-cache" title="永久链接至标题"></a></h3>
<p id="index-7">在导入搜索期间首先会被检查的地方是 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a>。 这个映射起到缓存之前导入的所有模块的作用(包括其中间路径)。 因此如果之前导入过 <code class="docutils literal notranslate"><span class="pre">foo.bar.baz</span></code>,则 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 将包含 <code class="docutils literal notranslate"><span class="pre">foo</span></code>, <code class="docutils literal notranslate"><span class="pre">foo.bar</span></code><code class="docutils literal notranslate"><span class="pre">foo.bar.baz</span></code> 条目。 每个键的值就是相应的模块对象。</p>
<p>在导入期间,会在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 查找模块名称,如存在则其关联的值就是需要导入的模块,导入过程完成。 然而,如果值为 <code class="docutils literal notranslate"><span class="pre">None</span></code>,则会引发 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a>。 如果找不到指定模块名称Python 将继续搜索该模块。</p>
<p><a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 是可写的。删除键可能不会破坏关联的模块(因为其他模块可能会保留对它的引用),但它会使命名模块的缓存条目无效,导致 Python 在下次导入时重新搜索命名模块。键也可以赋值为 <code class="docutils literal notranslate"><span class="pre">None</span></code> ,强制下一次导入模块导致 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a></p>
<p>但是要小心,因为如果你还保有对某个模块对象的引用,同时停用其在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中的缓存条目,然后又再次导入该名称的模块,则前后两个模块对象将 <em>不是</em> 同一个。 相反地,<a class="reference internal" href="../library/importlib.html#importlib.reload" title="importlib.reload"><code class="xref py py-func docutils literal notranslate"><span class="pre">importlib.reload()</span></code></a> 将重用 <em>同一个</em> 模块对象,并简单地通过重新运行模块的代码来重新初始化模块内容。</p>
</section>
<section id="finders-and-loaders">
<span id="id2"></span><h3><span class="section-number">5.3.2. </span>查找器和加载器<a class="headerlink" href="#finders-and-loaders" title="永久链接至标题"></a></h3>
<p id="index-8">如果指定名称的模块在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 找不到,则将发起调用 Python 的导入协议以查找和加载该模块。 此协议由两个概念性模块构成,即 <a class="reference internal" href="../glossary.html#term-finder"><span class="xref std std-term">查找器</span></a><a class="reference internal" href="../glossary.html#term-loader"><span class="xref std std-term">加载器</span></a>。 查找器的任务是确定是否能使用其所知的策略找到该名称的模块。 同时实现这两种接口的对象称为 <a class="reference internal" href="../glossary.html#term-importer"><span class="xref std std-term">导入器</span></a> —— 它们在确定能加载所需的模块时会返回其自身。</p>
<p>Python 包含了多个默认查找器和导入器。 第一个知道如何定位内置模块,第二个知道如何定位冻结模块。 第三个默认查找器会在 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a> 中搜索模块。 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a> 是一个由文件系统路径或 zip 文件组成的位置列表。 它还可以扩展为搜索任意可定位资源,例如由 URL 指定的资源。</p>
<p>导入机制是可扩展的,因此可以加入新的查找器以扩展模块搜索的范围和作用域。</p>
<p>查找器并不真正加载模块。 如果它们能找到指定名称的模块,会返回一个 <em class="dfn">模块规格说明</em>,这是对模块导入相关信息的封装,供后续导入机制用于在加载模块时使用。</p>
<p>以下各节描述了有关查找器和加载器协议的更多细节,包括你应该如何创建并注册新的此类对象来扩展导入机制。</p>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.4 版更改: </span>在之前的 Python 版本中,查找器会直接返回 <a class="reference internal" href="../glossary.html#term-loader"><span class="xref std std-term">加载器</span></a>,现在它们则返回模块规格说明,其中 <em>包含</em> 加载器。 加载器仍然在导入期间被使用,但负担的任务有所减少。</p>
</div>
</section>
<section id="import-hooks">
<h3><span class="section-number">5.3.3. </span>导入钩子<a class="headerlink" href="#import-hooks" title="永久链接至标题"></a></h3>
<p id="index-9">导入机制被设计为可扩展;其中的基本机制是 <em>导入钩子</em>。 导入钩子有两种类型: <em>元钩子</em><em>导入路径钩子</em></p>
<p>元钩子在导入过程开始时被调用,此时任何其他导入过程尚未发生,但 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 缓存查找除外。 这允许元钩子重载 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 过程、冻结模块甚至内置模块。 元钩子的注册是通过向 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 添加新的查找器对象,具体如下所述。</p>
<p>导入路径钩子是作为 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> (或 <code class="docutils literal notranslate"><span class="pre">package.__path__</span></code>) 过程的一部分,在遇到它们所关联的路径项的时候被调用。 导入路径钩子的注册是通过向 <a class="reference internal" href="../library/sys.html#sys.path_hooks" title="sys.path_hooks"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_hooks</span></code></a> 添加新的可调用对象,具体如下所述。</p>
</section>
<section id="the-meta-path">
<h3><span class="section-number">5.3.4. </span>元路径<a class="headerlink" href="#the-meta-path" title="永久链接至标题"></a></h3>
<p id="index-10">当指定名称的模块在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中找不到时Python 会接着搜索 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a>,其中包含元路径查找器对象列表。 这些查找器按顺序被查询以确定它们是否知道如何处理该名称的模块。 元路径查找器必须实现名为 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_spec" title="importlib.abc.MetaPathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 的方法,该方法接受三个参数:名称、导入路径和目标模块(可选)。 元路径查找器可使用任何策略来确定它是否能处理指定名称的模块。</p>
<p>如果元路径查找器知道如何处理指定名称的模块,它将返回一个说明对象。 如果它不能处理该名称的模块,则会返回 <code class="docutils literal notranslate"><span class="pre">None</span></code>。 如果 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 处理过程到达列表末尾仍未返回说明对象,则将引发 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a>。 任何其他被引发异常将直接向上传播,并放弃导入过程。</p>
<p>元路径查找器的 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_spec" title="importlib.abc.MetaPathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 方法调用带有两到三个参数。 第一个是被导入模块的完整限定名称,例如 <code class="docutils literal notranslate"><span class="pre">foo.bar.baz</span></code>。 第二个参数是供模块搜索使用的路径条目。 对于最高层级模块,第二个参数为 <code class="docutils literal notranslate"><span class="pre">None</span></code>,但对于子模块或子包,第二个参数为父包 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性的值。 如果相应的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性无法访问,将引发 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a>。 第三个参数是一个将被作为稍后加载目标的现有模块对象。 导入系统仅会在重加载期间传入一个目标模块。</p>
<p>对于单个导入请求可以多次遍历元路径。 例如,假设所涉及的模块都尚未被缓存,则导入 <code class="docutils literal notranslate"><span class="pre">foo.bar.baz</span></code> 将首先执行顶级的导入,在每个元路径查找器 (<code class="docutils literal notranslate"><span class="pre">mpf</span></code>) 上调用 <code class="docutils literal notranslate"><span class="pre">mpf.find_spec(&quot;foo&quot;,</span> <span class="pre">None,</span> <span class="pre">None)</span></code>。 在导入 <code class="docutils literal notranslate"><span class="pre">foo</span></code> 之后,<code class="docutils literal notranslate"><span class="pre">foo.bar</span></code> 将通过第二次遍历元路径来导入,调用 <code class="docutils literal notranslate"><span class="pre">mpf.find_spec(&quot;foo.bar&quot;,</span> <span class="pre">foo.__path__,</span> <span class="pre">None)</span></code>。 一旦 <code class="docutils literal notranslate"><span class="pre">foo.bar</span></code> 完成导入,最后一次遍历将调用 <code class="docutils literal notranslate"><span class="pre">mpf.find_spec(&quot;foo.bar.baz&quot;,</span> <span class="pre">foo.bar.__path__,</span> <span class="pre">None)</span></code></p>
<p>有些元路径查找器只支持顶级导入。 当把 <code class="docutils literal notranslate"><span class="pre">None</span></code> 以外的对象作为第三个参数传入时,这些导入器将总是返回 <code class="docutils literal notranslate"><span class="pre">None</span></code></p>
<p>Python 的默认 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 具有三种元路径查找器,一种知道如何导入内置模块,一种知道如何导入冻结模块,还有一种知道如何导入来自 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a> 的模块 (即 <a class="reference internal" href="../glossary.html#term-path-based-finder"><span class="xref std std-term">path based finder</span></a>)。</p>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.4 版更改: </span>元路径查找器的 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_spec" title="importlib.abc.MetaPathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 方法替代了 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_module" title="importlib.abc.MetaPathFinder.find_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_module()</span></code></a>,后者现已弃用,它将继续可用但不会再做改变,导入机制仅会在查找器未实现 <code class="docutils literal notranslate"><span class="pre">find_spec()</span></code> 时尝试使用它。</p>
</div>
</section>
</section>
<section id="loading">
<h2><span class="section-number">5.4. </span>加载<a class="headerlink" href="#loading" title="永久链接至标题"></a></h2>
<p>当一个模块说明被找到时,导入机制将在加载该模块时使用它(及其所包含的加载器)。 下面是导入的加载部分所发生过程的简要说明:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">module</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">spec</span><span class="o">.</span><span class="n">loader</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">spec</span><span class="o">.</span><span class="n">loader</span><span class="p">,</span> <span class="s1">&#39;create_module&#39;</span><span class="p">):</span>
<span class="c1"># It is assumed &#39;exec_module&#39; will also be defined on the loader.</span>
<span class="n">module</span> <span class="o">=</span> <span class="n">spec</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">create_module</span><span class="p">(</span><span class="n">spec</span><span class="p">)</span>
<span class="k">if</span> <span class="n">module</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">module</span> <span class="o">=</span> <span class="n">ModuleType</span><span class="p">(</span><span class="n">spec</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="c1"># The import-related module attributes get set here:</span>
<span class="n">_init_module_attrs</span><span class="p">(</span><span class="n">spec</span><span class="p">,</span> <span class="n">module</span><span class="p">)</span>
<span class="k">if</span> <span class="n">spec</span><span class="o">.</span><span class="n">loader</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># unsupported</span>
<span class="k">raise</span> <span class="ne">ImportError</span>
<span class="k">if</span> <span class="n">spec</span><span class="o">.</span><span class="n">origin</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">spec</span><span class="o">.</span><span class="n">submodule_search_locations</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># namespace package</span>
<span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="n">spec</span><span class="o">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">module</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">spec</span><span class="o">.</span><span class="n">loader</span><span class="p">,</span> <span class="s1">&#39;exec_module&#39;</span><span class="p">):</span>
<span class="n">module</span> <span class="o">=</span> <span class="n">spec</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">load_module</span><span class="p">(</span><span class="n">spec</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="c1"># Set __loader__ and __package__ if missing.</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="n">spec</span><span class="o">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">module</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">spec</span><span class="o">.</span><span class="n">loader</span><span class="o">.</span><span class="n">exec_module</span><span class="p">(</span><span class="n">module</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">BaseException</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">del</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="n">spec</span><span class="o">.</span><span class="n">name</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">raise</span>
<span class="k">return</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="n">spec</span><span class="o">.</span><span class="n">name</span><span class="p">]</span>
</pre></div>
</div>
<p>请注意以下细节:</p>
<blockquote>
<div><ul class="simple">
<li><p>如果在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中存在指定名称的模块对象,导入操作会已经将其返回。</p></li>
<li><p>在加载器执行模块代码之前,该模块将存在于 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中。 这一点很关键,因为该模块代码可能(直接或间接地)导入其自身;预先将其添加到 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 可防止在最坏情况下的无限递归和最好情况下的多次加载。</p></li>
<li><p>如果加载失败,则该模块 -- 只限加载失败的模块 -- 将从 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中移除。 任何已存在于 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 缓存的模块,以及任何作为附带影响被成功加载的模块仍会保留在缓存中。 这与重新加载不同,后者会把即使加载失败的模块也保留在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中。</p></li>
<li><p>在模块创建完成但还未执行之前,导入机制会设置导入相关模块属性(在上面的示例伪代码中为 “_init_module_attrs”详情参见 <a class="reference internal" href="#import-mod-attrs"><span class="std std-ref">后续部分</span></a></p></li>
<li><p>模块执行是加载的关键时刻,在此期间将填充模块的命名空间。 执行会完全委托给加载器,由加载器决定要填充的内容和方式。</p></li>
<li><p>在加载过程中创建并传递给 exec_module() 的模块并不一定就是在导入结束时返回的模块 <a class="footnote-reference brackets" href="#fnlo" id="id3">2</a></p></li>
</ul>
</div></blockquote>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.4 版更改: </span>导入系统已经接管了加载器建立样板的责任。 这些在以前是由 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.load_module" title="importlib.abc.Loader.load_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">importlib.abc.Loader.load_module()</span></code></a> 方法来执行的。</p>
</div>
<section id="loaders">
<h3><span class="section-number">5.4.1. </span>加载器<a class="headerlink" href="#loaders" title="永久链接至标题"></a></h3>
<p>模块加载器提供关键的加载功能:模块执行。 导入机制调用 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.exec_module" title="importlib.abc.Loader.exec_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">importlib.abc.Loader.exec_module()</span></code></a> 方法并传入一个参数来执行模块对象。 从 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.exec_module" title="importlib.abc.Loader.exec_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">exec_module()</span></code></a> 返回的任何值都将被忽略。</p>
<p>加载器必须满足下列要求:</p>
<blockquote>
<div><ul class="simple">
<li><p>如果模块是一个 Python 模块(而非内置模块或动态加载的扩展),加载器应该在模块的全局命名空间 (<code class="docutils literal notranslate"><span class="pre">module.__dict__</span></code>) 中执行模块的代码。</p></li>
<li><p>如果加载器无法执行指定模块,它应该引发 <a class="reference internal" href="../library/exceptions.html#ImportError" title="ImportError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ImportError</span></code></a>,不过在 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.exec_module" title="importlib.abc.Loader.exec_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">exec_module()</span></code></a> 期间引发的任何其他异常也会被传播。</p></li>
</ul>
</div></blockquote>
<p>在许多情况下,查找器和加载器可以是同一对象;在此情况下 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_spec" title="importlib.abc.MetaPathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 方法将返回一个规格说明,其中加载器会被设为 <code class="docutils literal notranslate"><span class="pre">self</span></code></p>
<p>模块加载器可以选择通过实现 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.create_module" title="importlib.abc.Loader.create_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">create_module()</span></code></a> 方法在加载期间创建模块对象。 它接受一个参数,即模块规格说明,并返回新的模块对象供加载期间使用。 <code class="docutils literal notranslate"><span class="pre">create_module()</span></code> 不需要在模块对象上设置任何属性。 如果模块返回 <code class="docutils literal notranslate"><span class="pre">None</span></code>,导入机制将自行创建新模块。</p>
<div class="versionadded">
<p><span class="versionmodified added">3.4 新版功能: </span>加载器的 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.create_module" title="importlib.abc.Loader.create_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">create_module()</span></code></a> 方法。</p>
</div>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.4 版更改: </span><a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.load_module" title="importlib.abc.Loader.load_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">load_module()</span></code></a> 方法被 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.exec_module" title="importlib.abc.Loader.exec_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">exec_module()</span></code></a> 所替代,导入机制会对加载的所有样板责任作出假定。</p>
<p>为了与现有的加载器兼容,导入机制会使用导入器的 <code class="docutils literal notranslate"><span class="pre">load_module()</span></code> 方法,如果它存在且导入器也未实现 <code class="docutils literal notranslate"><span class="pre">exec_module()</span></code>。 但是,<code class="docutils literal notranslate"><span class="pre">load_module()</span></code> 现已弃用,加载器应该转而实现 <code class="docutils literal notranslate"><span class="pre">exec_module()</span></code></p>
<p>除了执行模块之外,<code class="docutils literal notranslate"><span class="pre">load_module()</span></code> 方法必须实现上文描述的所有样板加载功能。 所有相同的限制仍然适用,并带有一些附加规定:</p>
<blockquote>
<div><ul class="simple">
<li><p>如果 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中存在指定名称的模块对象,加载器必须使用已存在的模块。 (否则 <a class="reference internal" href="../library/importlib.html#importlib.reload" title="importlib.reload"><code class="xref py py-func docutils literal notranslate"><span class="pre">importlib.reload()</span></code></a> 将无法正确工作。) 如果该名称模块不存在于 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中,加载器必须创建一个新的模块对象并将其加入 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a></p></li>
<li><p>在加载器执行模块代码之前,模块 <em>必须</em> 存在于 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 之中,以防止无限递归或多次加载。</p></li>
<li><p>如果加载失败,加载器必须移除任何它已加入到 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中的模块,但它必须 <strong>仅限</strong> 移除加载失败的模块,且所移除的模块应为加载器自身显式加载的。</p></li>
</ul>
</div></blockquote>
</div>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.5 版更改: </span><code class="docutils literal notranslate"><span class="pre">exec_module()</span></code> 已定义但 <code class="docutils literal notranslate"><span class="pre">create_module()</span></code> 未定义时将引发 <a class="reference internal" href="../library/exceptions.html#DeprecationWarning" title="DeprecationWarning"><code class="xref py py-exc docutils literal notranslate"><span class="pre">DeprecationWarning</span></code></a></p>
</div>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.6 版更改: </span><code class="docutils literal notranslate"><span class="pre">exec_module()</span></code> 已定义但 <code class="docutils literal notranslate"><span class="pre">create_module()</span></code> 未定义时将引发 <a class="reference internal" href="../library/exceptions.html#ImportError" title="ImportError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ImportError</span></code></a></p>
</div>
</section>
<section id="submodules">
<h3><span class="section-number">5.4.2. </span>子模块<a class="headerlink" href="#submodules" title="永久链接至标题"></a></h3>
<p>当使用任意机制 (例如 <code class="docutils literal notranslate"><span class="pre">importlib</span></code> API, <code class="docutils literal notranslate"><span class="pre">import</span></code><code class="docutils literal notranslate"><span class="pre">import-from</span></code> 语句或者内置的 <code class="docutils literal notranslate"><span class="pre">__import__()</span></code>) 加载一个子模块时,父模块的命名空间中会添加一个对子模块对象的绑定。 例如,如果包 <code class="docutils literal notranslate"><span class="pre">spam</span></code> 有一个子模块 <code class="docutils literal notranslate"><span class="pre">foo</span></code>,则在导入 <code class="docutils literal notranslate"><span class="pre">spam.foo</span></code> 之后,<code class="docutils literal notranslate"><span class="pre">spam</span></code> 将具有一个 绑定到相应子模块的 <code class="docutils literal notranslate"><span class="pre">foo</span></code> 属性。 假如现在有如下的目录结构:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">spam</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">foo</span><span class="o">.</span><span class="n">py</span>
<span class="n">bar</span><span class="o">.</span><span class="n">py</span>
</pre></div>
</div>
<p>并且 <code class="docutils literal notranslate"><span class="pre">spam/__init__.py</span></code> 中有如下几行内容:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">.foo</span> <span class="kn">import</span> <span class="n">Foo</span>
<span class="kn">from</span> <span class="nn">.bar</span> <span class="kn">import</span> <span class="n">Bar</span>
</pre></div>
</div>
<p>则执行如下代码将在 <code class="docutils literal notranslate"><span class="pre">spam</span></code> 模块中添加对 <code class="docutils literal notranslate"><span class="pre">foo</span></code><code class="docutils literal notranslate"><span class="pre">bar</span></code> 的名称绑定:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">spam</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">spam</span><span class="o">.</span><span class="n">foo</span>
<span class="go">&lt;module &#39;spam.foo&#39; from &#39;/tmp/imports/spam/foo.py&#39;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">spam</span><span class="o">.</span><span class="n">bar</span>
<span class="go">&lt;module &#39;spam.bar&#39; from &#39;/tmp/imports/spam/bar.py&#39;&gt;</span>
</pre></div>
</div>
<p>按照通常的 Python 名称绑定规则,这看起来可能会令人惊讶,但它实际上是导入系统的一个基本特性。 保持不变的一点是如果你有 <code class="docutils literal notranslate"><span class="pre">sys.modules['spam']</span></code><code class="docutils literal notranslate"><span class="pre">sys.modules['spam.foo']</span></code> (例如在上述导入之后就是如此),则后者必须显示为前者的 <code class="docutils literal notranslate"><span class="pre">foo</span></code> 属性。</p>
</section>
<section id="module-spec">
<h3><span class="section-number">5.4.3. </span>模块规格说明<a class="headerlink" href="#module-spec" title="永久链接至标题"></a></h3>
<p>导入机制在导入期间会使用有关每个模块的多种信息,特别是加载之前。 大多数信息都是所有模块通用的。 模块规格说明的目的是基于每个模块来封装这些导入相关信息。</p>
<p>在导入期间使用规格说明可允许状态在导入系统各组件之间传递,例如在创建模块规格说明的查找器和执行模块的加载器之间。 最重要的一点是,它允许导入机制执行加载的样板操作,在没有模块规格说明的情况下这是加载器的责任。</p>
<p>模块的规格说明会作为模块对象的 <code class="docutils literal notranslate"><span class="pre">__spec__</span></code> 属性对外公开。 有关模块规格的详细内容请参阅 <a class="reference internal" href="../library/importlib.html#importlib.machinery.ModuleSpec" title="importlib.machinery.ModuleSpec"><code class="xref py py-class docutils literal notranslate"><span class="pre">ModuleSpec</span></code></a></p>
<div class="versionadded">
<p><span class="versionmodified added">3.4 新版功能.</span></p>
</div>
</section>
<section id="import-related-module-attributes">
<span id="import-mod-attrs"></span><h3><span class="section-number">5.4.4. </span>导入相关的模块属性<a class="headerlink" href="#import-related-module-attributes" title="永久链接至标题"></a></h3>
<p>导入机制会在加载期间会根据模块的规格说明填充每个模块对象的这些属性,并在加载器执行模块之前完成。</p>
<dl class="attribute">
<dt id="__name__">
<code class="sig-name descname">__name__</code><a class="headerlink" href="#__name__" title="永久链接至目标"></a></dt>
<dd><p><code class="docutils literal notranslate"><span class="pre">__name__</span></code> 属性必须被设为模块的完整限定名称。 此名称被用来在导入系统中唯一地标识模块。</p>
</dd></dl>
<dl class="attribute">
<dt id="__loader__">
<code class="sig-name descname">__loader__</code><a class="headerlink" href="#__loader__" title="永久链接至目标"></a></dt>
<dd><p><code class="docutils literal notranslate"><span class="pre">__loader__</span></code> 属性必须被设为导入系统在加载模块时使用的加载器对象。 这主要是用于内省,但也可用于额外的加载器专用功能,例如获取关联到加载器的数据。</p>
</dd></dl>
<dl class="attribute">
<dt id="__package__">
<code class="sig-name descname">__package__</code><a class="headerlink" href="#__package__" title="永久链接至目标"></a></dt>
<dd><p>模块的 <code class="docutils literal notranslate"><span class="pre">__package__</span></code> 属性必须设定。 其取值必须为一个字符串,但可以与 <code class="docutils literal notranslate"><span class="pre">__name__</span></code> 取相同的值。 当模块是包时,其 <code class="docutils literal notranslate"><span class="pre">__package__</span></code> 值应该设为其 <code class="docutils literal notranslate"><span class="pre">__name__</span></code> 值。 当模块不是包时,对于最高层级模块 <code class="docutils literal notranslate"><span class="pre">__package__</span></code> 应该设为空字符串,对于子模块则应该设为其父包名。 更多详情可参阅 <span class="target" id="index-49"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0366"><strong>PEP 366</strong></a></p>
<p>该属性取代 <code class="docutils literal notranslate"><span class="pre">__name__</span></code> 被用来为主模块计算显式相对导入,相关定义见 <span class="target" id="index-50"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0366"><strong>PEP 366</strong></a>。 预期它与 <code class="docutils literal notranslate"><span class="pre">__spec__.parent</span></code> 具有相同的值。</p>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.6 版更改: </span><code class="docutils literal notranslate"><span class="pre">__package__</span></code> 预期与 <code class="docutils literal notranslate"><span class="pre">__spec__.parent</span></code> 具有相同的值。</p>
</div>
</dd></dl>
<dl class="attribute">
<dt id="__spec__">
<code class="sig-name descname">__spec__</code><a class="headerlink" href="#__spec__" title="永久链接至目标"></a></dt>
<dd><p><code class="docutils literal notranslate"><span class="pre">__spec__</span></code> 属性必须设为在导入模块时要使用的模块规格说明。 对 <code class="docutils literal notranslate"><span class="pre">__spec__</span></code> 的正确设定将同时作用于 <a class="reference internal" href="toplevel_components.html#programs"><span class="std std-ref">解释器启动期间初始化的模块</span></a>。 唯一的例外是 <code class="docutils literal notranslate"><span class="pre">__main__</span></code>,其中的 <code class="docutils literal notranslate"><span class="pre">__spec__</span></code><a class="reference internal" href="#main-spec"><span class="std std-ref">在某些情况下设为 None</span></a>.</p>
<p><code class="docutils literal notranslate"><span class="pre">__package__</span></code> 未定义时, <code class="docutils literal notranslate"><span class="pre">__spec__.parent</span></code> 会被用作回退项。</p>
<div class="versionadded">
<p><span class="versionmodified added">3.4 新版功能.</span></p>
</div>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.6 版更改: </span><code class="docutils literal notranslate"><span class="pre">__package__</span></code> 未定义时,<code class="docutils literal notranslate"><span class="pre">__spec__.parent</span></code> 会被用作回退项。</p>
</div>
</dd></dl>
<dl class="attribute">
<dt id="__path__">
<code class="sig-name descname">__path__</code><a class="headerlink" href="#__path__" title="永久链接至目标"></a></dt>
<dd><p>如果模块为包(不论是正规包还是命名空间包),则必须设置模块对象的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性。 属性值必须为可迭代对象,但如果 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 没有进一步的用处则可以为空。 如果 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 不为空,则在迭代时它应该产生字符串。 有关 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 语义的更多细节将在 <a class="reference internal" href="#package-path-rules"><span class="std std-ref">下文</span></a> 中给出。</p>
<p>不是包的模块不应该具有 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性。</p>
</dd></dl>
<dl class="attribute">
<dt id="__file__">
<code class="sig-name descname">__file__</code><a class="headerlink" href="#__file__" title="永久链接至目标"></a></dt>
<dd></dd></dl>
<dl class="attribute">
<dt id="__cached__">
<code class="sig-name descname">__cached__</code><a class="headerlink" href="#__cached__" title="永久链接至目标"></a></dt>
<dd><p><code class="docutils literal notranslate"><span class="pre">__file__</span></code> 是可选项。 如果设置,此属性的值必须为字符串。 导入系统可以选择在其没有语法意义时不设置 <code class="docutils literal notranslate"><span class="pre">__file__</span></code> (例如从数据库加载的模块)。</p>
<p>如果设置了 <code class="docutils literal notranslate"><span class="pre">__file__</span></code>,则也可以再设置 <code class="docutils literal notranslate"><span class="pre">__cached__</span></code> 属性,后者取值为编译版本代码(例如字节码文件)所在的路径。 设置此属性不要求文件已存在;该路径可以简单地指向应该存放编译文件的位置 (参见 <span class="target" id="index-51"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-3147"><strong>PEP 3147</strong></a>)。</p>
<p>当未设置 <code class="docutils literal notranslate"><span class="pre">__file__</span></code> 时也可以设置 <code class="docutils literal notranslate"><span class="pre">__cached__</span></code>。 但是,那样的场景很不典型。 最终,加载器会使用 <code class="docutils literal notranslate"><span class="pre">__file__</span></code> 和/或 <code class="docutils literal notranslate"><span class="pre">__cached__</span></code>。 因此如果一个加载器可以从缓存加载模块但是不能从文件加载,那种非典型场景就是适当的。</p>
</dd></dl>
</section>
<section id="module-path">
<span id="package-path-rules"></span><h3><span class="section-number">5.4.5. </span>module.__path__<a class="headerlink" href="#module-path" title="永久链接至标题"></a></h3>
<p>根据定义,如果一个模块具有 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性,它就是包。</p>
<p>包的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性会在导入其子包期间被使用。 在导入机制内部,它的功能与 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 基本相同,即在导入期间提供一个模块搜索位置列表。 但是,<code class="docutils literal notranslate"><span class="pre">__path__</span></code> 通常会比 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 受到更多限制。</p>
<p><code class="docutils literal notranslate"><span class="pre">__path__</span></code> 必须是由字符串组成的可迭代对象,但它也可以为空。 作用于 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 的规则同样适用于包的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code>,并且 <a class="reference internal" href="../library/sys.html#sys.path_hooks" title="sys.path_hooks"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_hooks</span></code></a> (见下文) 会在遍历包的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 时被查询。</p>
<p>包的 <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> 文件可以设置或更改包的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性,而且这是在 <span class="target" id="index-52"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a> 之前实现命名空间包的典型方式。 随着 <span class="target" id="index-53"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a> 的引入,命名空间包不再需要提供仅包含 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 操控代码的 <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> 文件;导入机制会自动为命名空间包正确地设置 <code class="docutils literal notranslate"><span class="pre">__path__</span></code></p>
</section>
<section id="module-reprs">
<h3><span class="section-number">5.4.6. </span>模块的 repr<a class="headerlink" href="#module-reprs" title="永久链接至标题"></a></h3>
<p>默认情况下,全部模块都具有一个可用的 repr但是你可以依据上述的属性设置在模块的规格说明中更为显式地控制模块对象的 repr。</p>
<p>如果模块具有 spec (<code class="docutils literal notranslate"><span class="pre">__spec__</span></code>),导入机制将尝试用它来生成一个 repr。 如果生成失败或找不到 spec导入系统将使用模块中的各种可用信息来制作一个默认 repr。 它将尝试使用 <code class="docutils literal notranslate"><span class="pre">module.__name__</span></code>, <code class="docutils literal notranslate"><span class="pre">module.__file__</span></code> 以及 <code class="docutils literal notranslate"><span class="pre">module.__loader__</span></code> 作为 repr 的输入,并将任何丢失的信息赋为默认值。</p>
<p>以下是所使用的确切规则:</p>
<blockquote>
<div><ul class="simple">
<li><p>如果模块具有 <code class="docutils literal notranslate"><span class="pre">__spec__</span></code> 属性,其中的规格信息会被用来生成 repr。 被查询的属性有 &quot;name&quot;, &quot;loader&quot;, &quot;origin&quot;&quot;has_location&quot; 等等。</p></li>
<li><p>如果模块具有 <code class="docutils literal notranslate"><span class="pre">__file__</span></code> 属性,这会被用作模块 repr 的一部分。</p></li>
<li><p>如果模块没有 <code class="docutils literal notranslate"><span class="pre">__file__</span></code> 但是有 <code class="docutils literal notranslate"><span class="pre">__loader__</span></code> 且取值不为 <code class="docutils literal notranslate"><span class="pre">None</span></code>,则加载器的 repr 会被用作模块 repr 的一部分。</p></li>
<li><p>对于其他情况,仅在 repr 中使用模块的 <code class="docutils literal notranslate"><span class="pre">__name__</span></code></p></li>
</ul>
</div></blockquote>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.4 版更改: </span><a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.module_repr" title="importlib.abc.Loader.module_repr"><code class="xref py py-meth docutils literal notranslate"><span class="pre">loader.module_repr()</span></code></a> 已弃用,导入机制现在使用模块规格说明来生成模块 repr。</p>
<p>为了向后兼容 Python 3.3,如果加载器定义了 <a class="reference internal" href="../library/importlib.html#importlib.abc.Loader.module_repr" title="importlib.abc.Loader.module_repr"><code class="xref py py-meth docutils literal notranslate"><span class="pre">module_repr()</span></code></a> 方法,则会在尝试上述两种方式之前先调用该方法来生成模块 repr。 但请注意此方法已弃用。</p>
</div>
</section>
<section id="cached-bytecode-invalidation">
<span id="pyc-invalidation"></span><h3><span class="section-number">5.4.7. </span>已缓存字节码的失效<a class="headerlink" href="#cached-bytecode-invalidation" title="永久链接至标题"></a></h3>
<p>在 Python 从 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件加载已缓存字节码之前,它会检查缓存是否由最新的 <code class="docutils literal notranslate"><span class="pre">.py</span></code> 源文件所生成。 默认情况下Python 通过在所写入缓存文件中保存源文件的最新修改时间戳和大小来实现这一点。 在运行时,导入系统会通过比对缓存文件中保存的元数据和源文件的元数据确定该缓存的有效性。</p>
<p>Python 也支持“基于哈希的”缓存文件,即保存源文件内容的哈希值而不是其元数据。 存在两种基于哈希的 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件:检查型和非检查型。 对于检查型基于哈希的 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件Python 会通过求哈希源文件并将结果哈希值与缓存文件中的哈希值比对来确定缓存有效性。 如果检查型基于哈希的缓存文件被确定为失效Python 会重新生成并写入一个新的检查型基于哈希的缓存文件。 对于非检查型 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件,只要其存在 Python 就会直接认定缓存文件有效。 确定基于哈希的 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件有效性的行为可通过 <a class="reference internal" href="../using/cmdline.html#cmdoption-check-hash-based-pycs"><code class="xref std std-option docutils literal notranslate"><span class="pre">--check-hash-based-pycs</span></code></a> 旗标来重载。</p>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.7 版更改: </span>增加了基于哈希的 <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件。在此之前Python 只支持基于时间戳来确定字节码缓存的有效性。</p>
</div>
</section>
</section>
<section id="the-path-based-finder">
<h2><span class="section-number">5.5. </span>基于路径的查找器<a class="headerlink" href="#the-path-based-finder" title="永久链接至标题"></a></h2>
<p id="index-16">在之前已经提及Python 带有几种默认的元路径查找器。 其中之一是 <a class="reference internal" href="../glossary.html#term-path-based-finder"><span class="xref std std-term">path based finder</span></a> (<a class="reference internal" href="../library/importlib.html#importlib.machinery.PathFinder" title="importlib.machinery.PathFinder"><code class="xref py py-class docutils literal notranslate"><span class="pre">PathFinder</span></code></a>),它会搜索包含一个 <a class="reference internal" href="../glossary.html#term-path-entry"><span class="xref std std-term">路径条目</span></a> 列表的 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a>。 每个路径条目指定一个用于搜索模块的位置。</p>
<p>基于路径的查找器自身并不知道如何进行导入。 它只是遍历单独的路径条目,将它们各自关联到某个知道如何处理特定类型路径的路径条目查找器。</p>
<p>默认的路径条目查找器集合实现了在文件系统中查找模块的所有语义,可处理多种特殊文件类型例如 Python 源码 (<code class="docutils literal notranslate"><span class="pre">.py</span></code> 文件)Python 字节码 (<code class="docutils literal notranslate"><span class="pre">.pyc</span></code> 文件) 以及共享库 (例如 <code class="docutils literal notranslate"><span class="pre">.so</span></code> 文件)。 在标准库中 <a class="reference internal" href="../library/zipimport.html#module-zipimport" title="zipimport: Support for importing Python modules from ZIP archives."><code class="xref py py-mod docutils literal notranslate"><span class="pre">zipimport</span></code></a> 模块的支持下,默认路径条目查找器还能处理所有来自 zip 文件的上述文件类型。</p>
<p>路径条目不必仅限于文件系统位置。 它们可以指向 URL、数据库查询或可以用字符串指定的任何其他位置。</p>
<p>基于路径的查找器还提供了额外的钩子和协议以便能扩展和定制可搜索路径条目的类型。 例如,如果你想要支持网络 URL 形式的路径条目,你可以编写一个实现 HTTP 语义在网络上查找模块的钩子。 这个钩子(可调用对象)应当返回一个支持下述协议的 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a>,以被用来获取一个专门针对来自网络的模块的加载器。</p>
<p>预先的警告:本节和上节都使用了 <em>查找器</em> 这一术语,并通过 <a class="reference internal" href="../glossary.html#term-meta-path-finder"><span class="xref std std-term">meta path finder</span></a><a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a> 两个术语来明确区分它们。 这两种类型的查找器非常相似,支持相似的协议,且在导入过程中以相似的方式运作,但关键的一点是要记住它们是有微妙差异的。 特别地,元路径查找器作用于导入过程的开始,主要是启动 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 遍历。</p>
<p>相比之下,路径条目查找器在某种意义上说是基于路径的查找器的实现细节,实际上,如果需要从 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 移除基于路径的查找器,并不会有任何路径条目查找器被发起调用。</p>
<section id="path-entry-finders">
<h3><span class="section-number">5.5.1. </span>路径条目查找器<a class="headerlink" href="#path-entry-finders" title="永久链接至标题"></a></h3>
<p id="index-17"><a class="reference internal" href="../glossary.html#term-path-based-finder"><span class="xref std std-term">path based finder</span></a> 会负责查找和加载通过 <a class="reference internal" href="../glossary.html#term-path-entry"><span class="xref std std-term">path entry</span></a> 字符串来指定位置的 Python 模块和包。 多数路径条目所指定的是文件系统中的位置,但它们并不必受限于此。</p>
<p>作为一种元路径查找器,<a class="reference internal" href="../glossary.html#term-path-based-finder"><span class="xref std std-term">path based finder</span></a> 实现了上文描述的 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_spec" title="importlib.abc.MetaPathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 协议,但是它还对外公开了一些附加钩子,可被用来定制模块如何从 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a> 查找和加载。</p>
<p>有三个变量由 <a class="reference internal" href="../glossary.html#term-path-based-finder"><span class="xref std std-term">path based finder</span></a>, <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a>, <a class="reference internal" href="../library/sys.html#sys.path_hooks" title="sys.path_hooks"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_hooks</span></code></a><a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> 所使用。 包对象的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性也会被使用。 它们提供了可用于定制导入机制的额外方式。</p>
<p><a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 包含一个提供模块和包搜索位置的字符串列表。 它初始化自 <code class="xref py py-data docutils literal notranslate"><span class="pre">PYTHONPATH</span></code> 环境变量以及多种其他特定安装和实现的默认设置。 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 条目可指定的名称有文件系统中的目录、zip 文件和其他可用于搜索模块的潜在“位置”(参见 <a class="reference internal" href="../library/site.html#module-site" title="site: Module responsible for site-specific configuration."><code class="xref py py-mod docutils literal notranslate"><span class="pre">site</span></code></a> 模块),例如 URL 或数据库查询等。 在 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 中只能出现字符串和字节串;所有其他数据类型都会被忽略。 字节串条目使用的编码由单独的 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">路径条目查找器</span></a> 来确定。</p>
<p><a class="reference internal" href="../glossary.html#term-path-based-finder"><span class="xref std std-term">path based finder</span></a> 是一种 <a class="reference internal" href="../glossary.html#term-meta-path-finder"><span class="xref std std-term">meta path finder</span></a>,因此导入机制会通过调用上文描述的基于路径的查找器的 <a class="reference internal" href="../library/importlib.html#importlib.machinery.PathFinder.find_spec" title="importlib.machinery.PathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 方法来启动 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a> 搜索。 当要向 <a class="reference internal" href="../library/importlib.html#importlib.machinery.PathFinder.find_spec" title="importlib.machinery.PathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 传入 <code class="docutils literal notranslate"><span class="pre">path</span></code> 参数时,它将是一个可遍历的字符串列表 —— 通常为用来在其内部进行导入的包的 <code class="docutils literal notranslate"><span class="pre">__path__</span></code> 属性。 如果 <code class="docutils literal notranslate"><span class="pre">path</span></code> 参数为 <code class="docutils literal notranslate"><span class="pre">None</span></code>,这表示最高层级的导入,将会使用 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a></p>
<p>基于路径的查找器会迭代搜索路径中的每个条目,并且每次都查找与路径条目对应的 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a> (<a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder" title="importlib.abc.PathEntryFinder"><code class="xref py py-class docutils literal notranslate"><span class="pre">PathEntryFinder</span></code></a>)。 因为这种操作可能很耗费资源(例如搜索会有 <cite>stat()</cite> 调用的开销),基于路径的查找器会维持一个缓存来将路径条目映射到路径条目查找器。 这个缓存放于 <a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> (尽管如此命名,但这个缓存实际存放的是查找器对象而非仅限于 <a class="reference internal" href="../glossary.html#term-importer"><span class="xref std std-term">importer</span></a> 对象)。 通过这种方式,对特定 <a class="reference internal" href="../glossary.html#term-path-entry"><span class="xref std std-term">path entry</span></a> 位置的 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a> 的高耗费搜索只需进行一次。 用户代码可以自由地从 <a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> 移除缓存条目,以强制基于路径的查找器再次执行路径条目搜索 <a class="footnote-reference brackets" href="#fnpic" id="id4">3</a></p>
<p>如果路径条目不存在于缓存中,基于路径的查找器会迭代 <a class="reference internal" href="../library/sys.html#sys.path_hooks" title="sys.path_hooks"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_hooks</span></code></a> 中的每个可调用对象。 对此列表中的每个 <a class="reference internal" href="../glossary.html#term-path-entry-hook"><span class="xref std std-term">路径条目钩子</span></a> 的调用会带有一个参数,即要搜索的路径条目。 每个可调用对象或是返回可处理路径条目的 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a>,或是引发 <a class="reference internal" href="../library/exceptions.html#ImportError" title="ImportError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ImportError</span></code></a>。 基于路径的查找器使用 <a class="reference internal" href="../library/exceptions.html#ImportError" title="ImportError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ImportError</span></code></a> 来表示钩子无法找到与 <a class="reference internal" href="../glossary.html#term-path-entry"><span class="xref std std-term">path entry</span></a> 相对应的 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a>。 该异常会被忽略并继续进行 <a class="reference internal" href="../glossary.html#term-import-path"><span class="xref std std-term">import path</span></a> 的迭代。 每个钩子应该期待接收一个字符串或字节串对象;字节串对象的编码由钩子决定(例如可以是文件系统使用的编码 UTF-8 或其它编码),如果钩子无法解码参数,它应该引发 <a class="reference internal" href="../library/exceptions.html#ImportError" title="ImportError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ImportError</span></code></a></p>
<p>如果 <a class="reference internal" href="../library/sys.html#sys.path_hooks" title="sys.path_hooks"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_hooks</span></code></a> 迭代结束时没有返回 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a>,则基于路径的查找器 <a class="reference internal" href="../library/importlib.html#importlib.machinery.PathFinder.find_spec" title="importlib.machinery.PathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 方法将在 <a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> 中存入 <code class="docutils literal notranslate"><span class="pre">None</span></code> (表示此路径条目没有对应的查找器) 并返回 <code class="docutils literal notranslate"><span class="pre">None</span></code>,表示此 <a class="reference internal" href="../glossary.html#term-meta-path-finder"><span class="xref std std-term">meta path finder</span></a> 无法找到该模块。</p>
<p>如果 <a class="reference internal" href="../library/sys.html#sys.path_hooks" title="sys.path_hooks"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_hooks</span></code></a> 中的某个 <a class="reference internal" href="../glossary.html#term-path-entry-hook"><span class="xref std std-term">path entry hook</span></a> 可调用对象的返回值 <em></em> 一个 <a class="reference internal" href="../glossary.html#term-path-entry-finder"><span class="xref std std-term">path entry finder</span></a>,则以下协议会被用来向查找器请求一个模块的规格说明,并在加载该模块时被使用。</p>
<p>当前工作目录 -- 由一个空字符串表示 -- 的处理方式与 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 中的其他条目略有不同。 首先,如果发现当前工作目录不存在,则 <a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> 中不会存放任何值。 其次,每个模块查找会对当前工作目录的值进行全新查找。 第三,由 <a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> 所使用并由 <a class="reference internal" href="../library/importlib.html#importlib.machinery.PathFinder.find_spec" title="importlib.machinery.PathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">importlib.machinery.PathFinder.find_spec()</span></code></a> 所返回的路径将是实际的当前工作目录而非空字符串。</p>
</section>
<section id="path-entry-finder-protocol">
<h3><span class="section-number">5.5.2. </span>路径条目查找器协议<a class="headerlink" href="#path-entry-finder-protocol" title="永久链接至标题"></a></h3>
<p>为了支持模块和已初始化包的导入,也为了给命名空间包提供组成部分,路径条目查找器必须实现 <a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder.find_spec" title="importlib.abc.PathEntryFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 方法。</p>
<p><a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder.find_spec" title="importlib.abc.PathEntryFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 接受两个参数,即要导入模块的完整限定名称,以及(可选的)目标模块。 <code class="docutils literal notranslate"><span class="pre">find_spec()</span></code> 返回模块的完全填充好的规格说明。 这个规格说明总是包含“加载器”集合(但有一个例外)。</p>
<p>为了向导入机制提示该规格说明代表一个命名空间 <a class="reference internal" href="../glossary.html#term-portion"><span class="xref std std-term">portion</span></a>,路径条目查找器会将 &quot;submodule_search_locations&quot; 设为一个包含该部分的列表。</p>
<div class="versionchanged">
<p><span class="versionmodified changed">在 3.4 版更改: </span><a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder.find_spec" title="importlib.abc.PathEntryFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 替代了 <a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder.find_loader" title="importlib.abc.PathEntryFinder.find_loader"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_loader()</span></code></a><a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder.find_module" title="importlib.abc.PathEntryFinder.find_module"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_module()</span></code></a>,后两者现在都已弃用,但会在 <code class="docutils literal notranslate"><span class="pre">find_spec()</span></code> 未定义时被使用。</p>
<p>较旧的路径条目查找器可能会实现这两个已弃用的方法中的一个而没有实现 <code class="docutils literal notranslate"><span class="pre">find_spec()</span></code>。 为保持向后兼容,这两个方法仍会被接受。 但是,如果在路径条目查找器上实现了 <code class="docutils literal notranslate"><span class="pre">find_spec()</span></code>,这两个遗留方法就会被忽略。</p>
<p><a class="reference internal" href="../library/importlib.html#importlib.abc.PathEntryFinder.find_loader" title="importlib.abc.PathEntryFinder.find_loader"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_loader()</span></code></a> 接受一个参数,即要导入模块的完整限定名称。 <code class="docutils literal notranslate"><span class="pre">find_loader()</span></code> 返回一个 2 元组,其中第一项是加载器而第二项是命名空间 <a class="reference internal" href="../glossary.html#term-portion"><span class="xref std std-term">portion</span></a></p>
<p>为了向后兼容其他导入协议的实现,许多路径条目查找器也同样支持元路径查找器所支持的传统 <code class="docutils literal notranslate"><span class="pre">find_module()</span></code> 方法。 但是路径条目查找器 <code class="docutils literal notranslate"><span class="pre">find_module()</span></code> 方法的调用绝不会带有 <code class="docutils literal notranslate"><span class="pre">path</span></code> 参数(它们被期望记录来自对路径钩子初始调用的恰当路径信息)。</p>
<p>路径条目查找器的 <code class="docutils literal notranslate"><span class="pre">find_module()</span></code> 方法已弃用,因为它不允许路径条目查找器为命名空间包提供部分。 如果 <code class="docutils literal notranslate"><span class="pre">find_loader()</span></code><code class="docutils literal notranslate"><span class="pre">find_module()</span></code> 同时存在于一个路径条目查找器中,导入系统将总是调用 <code class="docutils literal notranslate"><span class="pre">find_loader()</span></code> 而不选择 <code class="docutils literal notranslate"><span class="pre">find_module()</span></code></p>
</div>
</section>
</section>
<section id="replacing-the-standard-import-system">
<h2><span class="section-number">5.6. </span>替换标准导入系统<a class="headerlink" href="#replacing-the-standard-import-system" title="永久链接至标题"></a></h2>
<p>替换整个导入系统的最可靠机制是移除 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 的默认内容,,将其完全替换为自定义的元路径钩子。</p>
<p>一个可行的方式是仅改变导入语句的行为而不影响访问导入系统的其他 API那么替换内置的 <a class="reference internal" href="../library/functions.html#__import__" title="__import__"><code class="xref py py-func docutils literal notranslate"><span class="pre">__import__()</span></code></a> 函数可能就够了。 这种技巧也可以在模块层级上运用,即只在某个模块内部改变导入语句的行为。</p>
<p>想要选择性地预先防止在元路径上从一个钩子导入某些模块(而不是完全禁用标准导入系统),只需直接从 <a class="reference internal" href="../library/importlib.html#importlib.abc.MetaPathFinder.find_spec" title="importlib.abc.MetaPathFinder.find_spec"><code class="xref py py-meth docutils literal notranslate"><span class="pre">find_spec()</span></code></a> 引发 <a class="reference internal" href="../library/exceptions.html#ModuleNotFoundError" title="ModuleNotFoundError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">ModuleNotFoundError</span></code></a> 而非返回 <code class="docutils literal notranslate"><span class="pre">None</span></code> 就足够了。 返回后者表示元路径搜索应当继续,而引发异常则会立即终止搜索。</p>
</section>
<section id="package-relative-imports">
<span id="relativeimports"></span><h2><span class="section-number">5.7. </span>包相对导入<a class="headerlink" href="#package-relative-imports" title="永久链接至标题"></a></h2>
<p>相对导入使用前缀点号。 一个前缀点号表示相对导入从当前包开始。 两个或更多前缀点号表示对当前包的上级包的相对导入,第一个点号之后的每个点号代表一级。 例如,给定以下的包布局结构:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">package</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">subpackage1</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">moduleX</span><span class="o">.</span><span class="n">py</span>
<span class="n">moduleY</span><span class="o">.</span><span class="n">py</span>
<span class="n">subpackage2</span><span class="o">/</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">moduleZ</span><span class="o">.</span><span class="n">py</span>
<span class="n">moduleA</span><span class="o">.</span><span class="n">py</span>
</pre></div>
</div>
<p>不论是在 <code class="docutils literal notranslate"><span class="pre">subpackage1/moduleX.py</span></code> 还是 <code class="docutils literal notranslate"><span class="pre">subpackage1/__init__.py</span></code> 中,以下导入都是有效的:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">.moduleY</span> <span class="kn">import</span> <span class="n">spam</span>
<span class="kn">from</span> <span class="nn">.moduleY</span> <span class="kn">import</span> <span class="n">spam</span> <span class="k">as</span> <span class="n">ham</span>
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">moduleY</span>
<span class="kn">from</span> <span class="nn">..subpackage1</span> <span class="kn">import</span> <span class="n">moduleY</span>
<span class="kn">from</span> <span class="nn">..subpackage2.moduleZ</span> <span class="kn">import</span> <span class="n">eggs</span>
<span class="kn">from</span> <span class="nn">..moduleA</span> <span class="kn">import</span> <span class="n">foo</span>
</pre></div>
</div>
<p>绝对导入可以使用 <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">&lt;&gt;</span></code><code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">&lt;&gt;</span> <span class="pre">import</span> <span class="pre">&lt;&gt;</span></code> 语法,但相对导入只能使用第二种形式;其中的原因在于:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">XXX.YYY.ZZZ</span>
</pre></div>
</div>
<p>应当提供 <code class="docutils literal notranslate"><span class="pre">XXX.YYY.ZZZ</span></code> 作为可用表达式,但 .moduleY 不是一个有效的表达式。</p>
</section>
<section id="special-considerations-for-main">
<h2><span class="section-number">5.8. </span>有关 __main__ 的特殊事项<a class="headerlink" href="#special-considerations-for-main" title="永久链接至标题"></a></h2>
<p>对于 Python 的导入系统来说 <a class="reference internal" href="../library/__main__.html#module-__main__" title="__main__: The environment where the top-level script is run."><code class="xref py py-mod docutils literal notranslate"><span class="pre">__main__</span></code></a> 模块是一个特殊情况。 正如在 <a class="reference internal" href="toplevel_components.html#programs"><span class="std std-ref">另一节</span></a> 中所述,<code class="docutils literal notranslate"><span class="pre">__main__</span></code> 模块是在解释器启动时直接初始化的,与 <a class="reference internal" href="../library/sys.html#module-sys" title="sys: Access system-specific parameters and functions."><code class="xref py py-mod docutils literal notranslate"><span class="pre">sys</span></code></a><a class="reference internal" href="../library/builtins.html#module-builtins" title="builtins: The module that provides the built-in namespace."><code class="xref py py-mod docutils literal notranslate"><span class="pre">builtins</span></code></a> 很类似。 但是,与那两者不同,它并不被严格归类为内置模块。 这是因为 <code class="docutils literal notranslate"><span class="pre">__main__</span></code> 被初始化的方式依赖于发起调用解释器所附带的旗标和其他选项。</p>
<section id="main-spec">
<span id="id5"></span><h3><span class="section-number">5.8.1. </span>__main__.__spec__<a class="headerlink" href="#main-spec" title="永久链接至标题"></a></h3>
<p>根据 <a class="reference internal" href="../library/__main__.html#module-__main__" title="__main__: The environment where the top-level script is run."><code class="xref py py-mod docutils literal notranslate"><span class="pre">__main__</span></code></a> 被初始化的方式,<code class="docutils literal notranslate"><span class="pre">__main__.__spec__</span></code> 会被设置相应值或是 <code class="docutils literal notranslate"><span class="pre">None</span></code></p>
<p>当 Python 附加 <a class="reference internal" href="../using/cmdline.html#cmdoption-m"><code class="xref std std-option docutils literal notranslate"><span class="pre">-m</span></code></a> 选项启动时,<code class="docutils literal notranslate"><span class="pre">__spec__</span></code> 会被设为相应模块或包的模块规格说明。 <code class="docutils literal notranslate"><span class="pre">__spec__</span></code> 也会在 <code class="docutils literal notranslate"><span class="pre">__main__</span></code> 模块作为执行某个目录zip 文件或其它 <a class="reference internal" href="../library/sys.html#sys.path" title="sys.path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path</span></code></a> 条目的一部分加载时被填充。</p>
<p><a class="reference internal" href="../using/cmdline.html#using-on-interface-options"><span class="std std-ref">其余的情况</span></a><code class="docutils literal notranslate"><span class="pre">__main__.__spec__</span></code> 会被设为 <code class="docutils literal notranslate"><span class="pre">None</span></code>,因为用于填充 <a class="reference internal" href="../library/__main__.html#module-__main__" title="__main__: The environment where the top-level script is run."><code class="xref py py-mod docutils literal notranslate"><span class="pre">__main__</span></code></a> 的代码不直接与可导入的模块相对应:</p>
<ul class="simple">
<li><p>交互型提示</p></li>
<li><p><a class="reference internal" href="../using/cmdline.html#cmdoption-c"><code class="xref std std-option docutils literal notranslate"><span class="pre">-c</span></code></a> 选项</p></li>
<li><p>从 stdin 运行</p></li>
<li><p>直接从源码或字节码文件运行</p></li>
</ul>
<p>请注意在最后一种情况中 <code class="docutils literal notranslate"><span class="pre">__main__.__spec__</span></code> 总是为 <code class="docutils literal notranslate"><span class="pre">None</span></code><em>即使</em> 文件从技术上说可以作为一个模块被导入。 如果想要让 <a class="reference internal" href="../library/__main__.html#module-__main__" title="__main__: The environment where the top-level script is run."><code class="xref py py-mod docutils literal notranslate"><span class="pre">__main__</span></code></a> 中的元数据生效,请使用 <a class="reference internal" href="../using/cmdline.html#cmdoption-m"><code class="xref std std-option docutils literal notranslate"><span class="pre">-m</span></code></a> 开关。</p>
<p>还要注意即使是在 <code class="docutils literal notranslate"><span class="pre">__main__</span></code> 对应于一个可导入模块且 <code class="docutils literal notranslate"><span class="pre">__main__.__spec__</span></code> 被相应地设定时,它们仍会被视为 <em>不同的</em> 模块。 这是由于以下事实:使用 <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">&quot;__main__&quot;:</span></code> 检测来保护的代码块仅会在模块被用来填充 <code class="docutils literal notranslate"><span class="pre">__main__</span></code> 命名空间时而非普通的导入时被执行。</p>
</section>
</section>
<section id="open-issues">
<h2><span class="section-number">5.9. </span>开放问题项<a class="headerlink" href="#open-issues" title="永久链接至标题"></a></h2>
<p>XXX 最好是能增加一个图表。</p>
<p>XXX * (import_machinery.rst) 是否要专门增加一节来说明模块和包的属性,也许可以扩展或移植数据模型参考页中的相关条目?</p>
<p>XXX 库手册中的 runpy 和 pkgutil 等等应该都在页面顶端增加指向新的导入系统章节的“另请参阅”链接。</p>
<p>XXX 是否要增加关于初始化 <code class="docutils literal notranslate"><span class="pre">__main__</span></code> 的不同方式的更多解释?</p>
<p>XXX 增加更多有关 <code class="docutils literal notranslate"><span class="pre">__main__</span></code> 怪异/坑人特性的信息 (例如直接从 <span class="target" id="index-54"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0395"><strong>PEP 395</strong></a> 复制)。</p>
</section>
<section id="references">
<h2><span class="section-number">5.10. </span>参考文献<a class="headerlink" href="#references" title="永久链接至标题"></a></h2>
<p>导入机制自 Python 诞生之初至今已发生了很大的变化。 原始的 <a class="reference external" href="https://www.python.org/doc/essays/packages/">包规格说明</a> 仍然可以查阅,但在撰写该文档之后许多相关细节已被修改。</p>
<p>原始的 <a class="reference internal" href="../library/sys.html#sys.meta_path" title="sys.meta_path"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.meta_path</span></code></a> 规格说明见 <span class="target" id="index-55"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0302"><strong>PEP 302</strong></a>,后续的扩展说明见 <span class="target" id="index-56"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a></p>
<p><span class="target" id="index-57"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a> 为 Python 3.3 引入了 <a class="reference internal" href="../glossary.html#term-namespace-package"><span class="xref std std-term">命名空间包</span></a><span class="target" id="index-58"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0420"><strong>PEP 420</strong></a> 还引入了 <code class="xref py py-meth docutils literal notranslate"><span class="pre">find_loader()</span></code> 协议作为 <code class="xref py py-meth docutils literal notranslate"><span class="pre">find_module()</span></code> 的替代。</p>
<p><span class="target" id="index-59"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0366"><strong>PEP 366</strong></a> 描述了新增的 <code class="docutils literal notranslate"><span class="pre">__package__</span></code> 属性,用于在模块中的显式相对导入。</p>
<p><span class="target" id="index-60"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0328"><strong>PEP 328</strong></a> 引入了绝对和显式相对导入,并初次提出了 <code class="docutils literal notranslate"><span class="pre">__name__</span></code> 语义,最终由 <span class="target" id="index-61"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0366"><strong>PEP 366</strong></a><code class="docutils literal notranslate"><span class="pre">__package__</span></code> 加入规范描述。</p>
<p><span class="target" id="index-62"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0338"><strong>PEP 338</strong></a> 定义了将模块作为脚本执行。</p>
<p><span class="target" id="index-63"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0451"><strong>PEP 451</strong></a> 在 spec 对象中增加了对每个模块导入状态的封装。 它还将加载器的大部分样板责任移交回导入机制中。 这些改变允许弃用导入系统中的一些 API 并为查找器和加载器增加一些新的方法。</p>
<p class="rubric">备注</p>
<dl class="footnote brackets">
<dt class="label" id="fnmo"><span class="brackets"><a class="fn-backref" href="#id1">1</a></span></dt>
<dd><p>参见 <a class="reference internal" href="../library/types.html#types.ModuleType" title="types.ModuleType"><code class="xref py py-class docutils literal notranslate"><span class="pre">types.ModuleType</span></code></a></p>
</dd>
<dt class="label" id="fnlo"><span class="brackets"><a class="fn-backref" href="#id3">2</a></span></dt>
<dd><p>importlib 实现避免直接使用返回值。 而是通过在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中查找模块名称来获取模块对象。 这种方式的间接影响是被导入的模块可能在 <a class="reference internal" href="../library/sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.modules</span></code></a> 中替换其自身。 这属于具体实现的特定行为,不保证能在其他 Python 实现中起作用。</p>
</dd>
<dt class="label" id="fnpic"><span class="brackets"><a class="fn-backref" href="#id4">3</a></span></dt>
<dd><p>在遗留代码中,有可能在 <a class="reference internal" href="../library/sys.html#sys.path_importer_cache" title="sys.path_importer_cache"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code></a> 中找到 <a class="reference internal" href="../library/imp.html#imp.NullImporter" title="imp.NullImporter"><code class="xref py py-class docutils literal notranslate"><span class="pre">imp.NullImporter</span></code></a> 的实例。 建议将这些代码修改为使用 <code class="docutils literal notranslate"><span class="pre">None</span></code> 代替。 详情参见 <a class="reference internal" href="../whatsnew/3.3.html#portingpythoncode"><span class="std std-ref">移植 Python 代码</span></a></p>
</dd>
</dl>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="../contents.html">目录</a></h3>
<ul>
<li><a class="reference internal" href="#">5. 导入系统</a><ul>
<li><a class="reference internal" href="#importlib">5.1. <code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib</span></code></a></li>
<li><a class="reference internal" href="#packages">5.2. 包</a><ul>
<li><a class="reference internal" href="#regular-packages">5.2.1. 常规包</a></li>
<li><a class="reference internal" href="#namespace-packages">5.2.2. 命名空间包</a></li>
</ul>
</li>
<li><a class="reference internal" href="#searching">5.3. 搜索</a><ul>
<li><a class="reference internal" href="#the-module-cache">5.3.1. 模块缓存</a></li>
<li><a class="reference internal" href="#finders-and-loaders">5.3.2. 查找器和加载器</a></li>
<li><a class="reference internal" href="#import-hooks">5.3.3. 导入钩子</a></li>
<li><a class="reference internal" href="#the-meta-path">5.3.4. 元路径</a></li>
</ul>
</li>
<li><a class="reference internal" href="#loading">5.4. 加载</a><ul>
<li><a class="reference internal" href="#loaders">5.4.1. 加载器</a></li>
<li><a class="reference internal" href="#submodules">5.4.2. 子模块</a></li>
<li><a class="reference internal" href="#module-spec">5.4.3. 模块规格说明</a></li>
<li><a class="reference internal" href="#import-related-module-attributes">5.4.4. 导入相关的模块属性</a></li>
<li><a class="reference internal" href="#module-path">5.4.5. module.__path__</a></li>
<li><a class="reference internal" href="#module-reprs">5.4.6. 模块的 repr</a></li>
<li><a class="reference internal" href="#cached-bytecode-invalidation">5.4.7. 已缓存字节码的失效</a></li>
</ul>
</li>
<li><a class="reference internal" href="#the-path-based-finder">5.5. 基于路径的查找器</a><ul>
<li><a class="reference internal" href="#path-entry-finders">5.5.1. 路径条目查找器</a></li>
<li><a class="reference internal" href="#path-entry-finder-protocol">5.5.2. 路径条目查找器协议</a></li>
</ul>
</li>
<li><a class="reference internal" href="#replacing-the-standard-import-system">5.6. 替换标准导入系统</a></li>
<li><a class="reference internal" href="#package-relative-imports">5.7. 包相对导入</a></li>
<li><a class="reference internal" href="#special-considerations-for-main">5.8. 有关 __main__ 的特殊事项</a><ul>
<li><a class="reference internal" href="#main-spec">5.8.1. __main__.__spec__</a></li>
</ul>
</li>
<li><a class="reference internal" href="#open-issues">5.9. 开放问题项</a></li>
<li><a class="reference internal" href="#references">5.10. 参考文献</a></li>
</ul>
</li>
</ul>
<h4>上一个主题</h4>
<p class="topless"><a href="executionmodel.html"
title="上一章"><span class="section-number">4. </span>执行模型</a></p>
<h4>下一个主题</h4>
<p class="topless"><a href="expressions.html"
title="下一章"><span class="section-number">6. </span>表达式</a></p>
<div role="note" aria-label="source link">
<h3>本页</h3>
<ul class="this-page-menu">
<li><a href="../bugs.html">报告 Bug</a></li>
<li>
<a href="https://github.com/python/cpython/blob/3.8/Doc/reference/import.rst"
rel="nofollow">显示源代码
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>导航</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="总目录"
>索引</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python 模块索引"
>模块</a> |</li>
<li class="right" >
<a href="expressions.html" title="6. 表达式"
>下一页</a> |</li>
<li class="right" >
<a href="executionmodel.html" title="4. 执行模型"
>上一页</a> |</li>
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> &#187;</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.8.20 Documentation</a> &#187;
</li>
<li class="nav-item nav-item-1"><a href="index.html" >Python语言参考</a> &#187;</li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
<input type="submit" value="转向" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
|
</li>
</ul>
</div>
<div class="footer">
&copy; <a href="../copyright.html">版权所有</a> 2001-2024, Python Software Foundation.
<br />
This page is licensed under the Python Software Foundation License Version 2.
<br />
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
<br />
<br />
The Python Software Foundation is a non-profit corporation.
<a href="https://www.python.org/psf/donations/">Please donate.</a>
<br />
<br />
最后更新于 12月 09, 2024.
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
<br />
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 2.4.4.
</div>
</body>
</html>