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

476 lines
47 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>将 Python 2 代码迁移到 Python 3 &#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="将扩展模块移植到 Python 3" href="cporting.html" />
<link rel="prev" title="Python 指南" href="index.html" />
<link rel="canonical" href="https://docs.python.org/3/howto/pyporting.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="#">将 Python 2 代码迁移到 Python 3</a><ul>
<li><a class="reference internal" href="#the-short-explanation">简要说明</a></li>
<li><a class="reference internal" href="#details">详情</a><ul>
<li><a class="reference internal" href="#drop-support-for-python-2-6-and-older">删除对Python 2.6及更早版本的支持</a></li>
<li><a class="reference internal" href="#make-sure-you-specify-the-proper-version-support-in-your-setup-py-file">确保你在你的``setup.py``文件中指定适当的版本支持</a></li>
<li><a class="reference internal" href="#have-good-test-coverage">良好的测试覆盖率</a></li>
<li><a class="reference internal" href="#learn-the-differences-between-python-2-3">了解Python 2 和 3之间的区别</a></li>
<li><a class="reference internal" href="#update-your-code">更新代码</a><ul>
<li><a class="reference internal" href="#division">除法</a></li>
<li><a class="reference internal" href="#text-versus-binary-data">文本与二进制数据</a></li>
<li><a class="reference internal" href="#use-feature-detection-instead-of-version-detection">使用特征检测而不是版本检测</a></li>
</ul>
</li>
<li><a class="reference internal" href="#prevent-compatibility-regressions">防止兼容性退步</a></li>
<li><a class="reference internal" href="#check-which-dependencies-block-your-transition">检查哪些依赖性会阻碍你的过渡</a></li>
<li><a class="reference internal" href="#update-your-setup-py-file-to-denote-python-3-compatibility">更新你的``setup.py``文件以表示对Python 3的兼容</a></li>
<li><a class="reference internal" href="#use-continuous-integration-to-stay-compatible">使用持续集成以保持兼容</a></li>
<li><a class="reference internal" href="#consider-using-optional-static-type-checking">考虑使用可选的静态类型检查</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>上一个主题</h4>
<p class="topless"><a href="index.html"
title="上一章">Python 指南</a></p>
<h4>下一个主题</h4>
<p class="topless"><a href="cporting.html"
title="下一章">将扩展模块移植到 Python 3</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/howto/pyporting.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/howto/pyporting.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="cporting.html" title="将扩展模块移植到 Python 3"
accesskey="N">下一页</a> |</li>
<li class="right" >
<a href="index.html" title="Python 指南"
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="porting-python-2-code-to-python-3">
<span id="pyporting-howto"></span><h1>将 Python 2 代码迁移到 Python 3<a class="headerlink" href="#porting-python-2-code-to-python-3" title="永久链接至标题"></a></h1>
<dl class="field-list simple">
<dt class="field-odd">作者</dt>
<dd class="field-odd"><p>Brett Cannon</p>
</dd>
</dl>
<div class="topic">
<p class="topic-title">摘要</p>
<p>Python 3 是 Python 的未来,但 Python 2 仍处于活跃使用阶段最好让您的项目在两个主要版本的Python 上都可用。本指南旨在帮助您了解如何最好地同时支持 Python 2 和 3。</p>
<p>如果您希望迁移扩展模块而不是纯 Python 代码,请参阅 <a class="reference internal" href="cporting.html#cporting-howto"><span class="std std-ref">将扩展模块移植到 Python 3</span></a></p>
<p>如果你想了解核心 Python 开发者对于 Python 3 的出现有何看法,你可以阅读 Nick Coghlan 的 <a class="reference external" href="https://ncoghlan-devs-python-notes.readthedocs.io/en/latest/python3/questions_and_answers.html">Python 3 Q &amp; A</a> 或 Brett Cannon 的 <a class="reference external" href="https://snarky.ca/why-python-3-exists">为什么要有 Python 3</a>.</p>
<p>有关迁移的帮助,您可以通过电子邮件向 <a class="reference external" href="https://mail.python.org/mailman/listinfo/python-porting">python-porting</a> 邮件列表发送问题。</p>
</div>
<section id="the-short-explanation">
<h2>简要说明<a class="headerlink" href="#the-short-explanation" title="永久链接至标题"></a></h2>
<p>为了使你的项目与单源Python 2/3兼容基本步骤如下:</p>
<ol class="arabic simple">
<li><p>只担心支持Python 2.7的问题</p></li>
<li><p>确保有良好的测试覆盖率(可以用 <a class="reference external" href="https://pypi.org/project/coverage">coverage.py</a> 来完成此任务;用 <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">coverage</span></code> 安装)</p></li>
<li><p>了解Python 2 和 3之间的区别</p></li>
<li><p>使用 <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a> (<a class="reference external" href="https://python-modernize.readthedocs.io/">或Modernize</a>) 来更新你的代码 (例如``python -m pip install future``)。</p></li>
<li><p>Use <a class="reference external" href="https://pypi.org/project/pylint">Pylint</a> to help make sure you don't regress on your Python 3 support
(<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">pylint</span></code>)</p></li>
<li><p>使用 <a class="reference external" href="https://pypi.org/project/caniusepython3">caniusepython3</a> 来找出你的哪些依赖关系阻碍了你对 Python 3 的使用 (<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">caniusepython3</span></code>)</p></li>
<li><p>一旦你的依赖关系不再阻碍你,使用持续集成来确保你与 Python 2 和 3 保持兼容 (<a class="reference external" href="https://pypi.org/project/tox">tox</a> 可以帮助对多个版本的 Python 进行测试; <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">tox</span></code>)</p></li>
<li><p>考虑使用可选的静态类型检查来确保你的类型用法在 Python 2 和 3 中都适用 (例如使用 <a class="reference external" href="http://mypy-lang.org/">mypy</a> 来检查你在 Python 2 和 Python 3 中的类型)。</p></li>
</ol>
</section>
<section id="details">
<h2>详情<a class="headerlink" href="#details" title="永久链接至标题"></a></h2>
<p>同时支持Python 2和3的一个关键点是你可以从**今天**开始即使你的依赖关系还不支持Python 3也不意味着你不能现在就对你的代码进行现代化改造以支持Python 3。支持Python 3所需的大多数变化都会使代码更干净甚至在Python 2的代码中也会使用更新的做法。</p>
<p>另一个关键点是使你的Python 2代码现代化以支持Python 3在很大程度上是为你自动化的。虽然由于Python 3清晰区分了文本数据与二进制数据你可能必须做出一些API决定。但现在已经为你完成了大部分低级别的工作因此至少可以立即从自动化变化中受益。</p>
<p>当你继续阅读关于迁移你的代码以同时支持Python 2和3的细节时请牢记这些关键点。</p>
<section id="drop-support-for-python-2-6-and-older">
<h3>删除对Python 2.6及更早版本的支持<a class="headerlink" href="#drop-support-for-python-2-6-and-older" title="永久链接至标题"></a></h3>
<p>虽然你可以让 Python 2.5 与 Python 3 一起工作,但如果你只需要与 Python 2.7 一起工作,那就会 <strong>更加容易</strong>。 如果放弃 Python 2.5 不是一种选择,那么 <a class="reference external" href="https://pypi.org/project/six">six</a> 项目可以帮助你同时支持 Python 2.5 和 3 (<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">six</span></code>)。 不过请注意,本 HOWTO 中列出的几乎所有项目都有可能会不再可用。</p>
<p>如果你能够跳过Python 2.5和更早的版本那么对你的代码所做的必要修改应该看起来和感觉上都是你已经习惯的Python代码。在最坏的情况下你将不得不使用一个函数而不是一个实例方法或者不得不导入一个函数而不是使用一个内置的函数但除此之外整体的转变应该不会让你感到陌生。</p>
<p>但你的目标应该是只支持 Python 2.7。Python 2.6 不再被免费支持,因此也就没有得到错误修复。这意味着**你**必须解决你在Python 2.6上遇到的任何问题。本HOWTO中提到的一些工具也不支持Python 2.6 (例如,<a class="reference external" href="https://pypi.org/project/pylint">Pylint</a>)随着时间的推移这将变得越来越普遍。如果你只支持你必须支持的Python版本那么对你来说会更容易。</p>
</section>
<section id="make-sure-you-specify-the-proper-version-support-in-your-setup-py-file">
<h3>确保你在你的``setup.py``文件中指定适当的版本支持<a class="headerlink" href="#make-sure-you-specify-the-proper-version-support-in-your-setup-py-file" title="永久链接至标题"></a></h3>
<p>在你的``setup.py``文件中,你应该有适当的 <a class="reference external" href="https://pypi.org/classifiers">trove classifier</a> 指定你支持哪些版本的 Python。由于你的项目还不支持 Python 3你至少应该指定``Programming Language :: Python :: 2 :: Only``。理想情况下你还应该指定你所支持的Python的每个主要/次要版本,例如:<code class="docutils literal notranslate"><span class="pre">Programming</span> <span class="pre">Language</span> <span class="pre">::</span> <span class="pre">Python</span> <span class="pre">::</span> <span class="pre">2.7</span></code>。.</p>
</section>
<section id="have-good-test-coverage">
<h3>良好的测试覆盖率<a class="headerlink" href="#have-good-test-coverage" title="永久链接至标题"></a></h3>
<p>一旦你的代码支持了你希望的Python 2的最老版本你将希望确保你的测试套件有良好的覆盖率。一个好的经验法则是如果你想对你的测试套件有足够的信心在让工具重写你的代码后出现的任何故障都是工具中的实际错误而不是你的代码中的错误。如果你想要一个目标数字试着获得超过80%的覆盖率如果你发现很难获得好于90%的覆盖率,也不要感到遗憾)。如果你还没有一个测量测试覆盖率的工具,<a class="reference external" href="https://pypi.org/project/coverage">那么推荐使用coverage.py</a></p>
</section>
<section id="learn-the-differences-between-python-2-3">
<h3>了解Python 2 和 3之间的区别<a class="headerlink" href="#learn-the-differences-between-python-2-3" title="永久链接至标题"></a></h3>
<p>一旦你的代码经过了很好的测试,你就可以开始把你的代码移植到 Python 3 上了! 但是为了充分了解你的代码将发生怎样的变化,以及你在编码时要注意什么,你将会想要了解 Python 3 相比 Python 2 的变化。 通常来说,两个最好的方法是阅读 Python 3 每个版本的 <a class="reference internal" href="../whatsnew/index.html#whatsnew-index"><span class="std std-ref">&quot;What's New&quot;</span></a> 文档和 <a class="reference external" href="http://python3porting.com/">Porting to Python 3</a> 书(在线免费版本)。还有一个来自 Python-Future 项目的便利 <a class="reference external" href="http://python-future.org/compatible_idioms.html">cheat sheet</a></p>
</section>
<section id="update-your-code">
<h3>更新代码<a class="headerlink" href="#update-your-code" title="永久链接至标题"></a></h3>
<p>一旦你觉得你知道了 Python 3 与 Python 2 相比有什么不同,就是时候更新你的代码了! 你可以选择两种工具来自动移植你的代码: <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a><a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a>。 你选择哪个工具将取决于你希望你的代码有多像 Python 3。 <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a> 尽力使 Python 3 的习性和做法在 Python 2 中存在,例如,从 Python 3 中回传 <code class="docutils literal notranslate"><span class="pre">bytes</span></code> 类型,这样你就可以在 Python 的主要版本之间实现语义上的平等。 另一方面,<a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a> 更加保守,它针对的是 Python 2/3 的子集,直接依靠 <a class="reference external" href="https://pypi.org/project/six">six</a> 来帮助提供兼容性。 由于 Python 3 是未来,最好考虑 Futurize以开始适应 Python 3 所引入的、你还不习惯的任何新做法。</p>
<p>无论你选择哪种工具它们都会更新你的代码使之在Python 3下运行同时与你开始使用的Python 2版本保持兼容。根据你想要的保守程度你可能想先在你的测试套件上运行该工具并目测差异以确保转换的准确性。在你转换了你的测试套件并验证了所有的测试仍然如期通过后你就可以转换你的应用程序代码了因为你知道任何测试失败都是转译的错误。</p>
<p>不幸的是这些工具不能自动地使你的代码在Python 3下工作因此有一些东西你需要手动更新以获得对Python 3的完全支持这些步骤在不同的工具中是必要的。阅读你选择使用的工具的文档看看它默认修复了什么以及它可以选择做什么以了解哪些将为你修复哪些你可能必须自己修复例如在Modernize中使用``io.open()``覆写内置的``open()``函数默认是关闭的)。不过幸运的是,只有几件需要注意的事情,可以说是大问题,如果不注意的话,可能很难调试。</p>
<section id="division">
<h4>除法<a class="headerlink" href="#division" title="永久链接至标题"></a></h4>
<p>在Python 3中<code class="docutils literal notranslate"><span class="pre">5</span> <span class="pre">/</span> <span class="pre">2</span> <span class="pre">==</span> <span class="pre">2.5``而不是``2</span></code>;所有``int``数值之间的除法都会产生一个``float``、这个变化实际上从2002年发布的Python 2.2开始就已经计划好了。从那时起,我们就鼓励用户在所有使用``/<code class="docutils literal notranslate"><span class="pre"></span></code>//<code class="docutils literal notranslate"><span class="pre">运算符的文件中添加``from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">division</span></code>,或者在运行解释器时使用``-Q``标志。如果你没有这样做,那么你需要检查你的代码并做两件事。</p>
<ol class="arabic simple">
<li><p>添加 <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">division</span></code> 到你的文件。</p></li>
<li><p>根据需要更新任何除法运算符,要么使用 <code class="docutils literal notranslate"><span class="pre">//</span></code> 来使用向下取整除法,要么继续使用 <code class="docutils literal notranslate"><span class="pre">/</span></code> 并得到一个浮点数</p></li>
</ol>
<p>之所以没有简单地将 <code class="docutils literal notranslate"><span class="pre">/</span></code> 自动翻译成 <code class="docutils literal notranslate"><span class="pre">//</span></code>,是因为如果一个对象定义了一个 <code class="docutils literal notranslate"><span class="pre">__truediv__</span></code> 方法,但没有定义 <code class="docutils literal notranslate"><span class="pre">__floordiv__</span></code>,那么你的代码就会运行失败(例如,一个用户定义的类用 <code class="docutils literal notranslate"><span class="pre">/</span></code> 来表示一些操作,但没有用 <code class="docutils literal notranslate"><span class="pre">//</span></code> 来表示同样的事情或根本没有定义)。</p>
</section>
<section id="text-versus-binary-data">
<h4>文本与二进制数据<a class="headerlink" href="#text-versus-binary-data" title="永久链接至标题"></a></h4>
<p>在Python 2中你可以对文本和二进制数据都使用``str``类型。不幸的是,这两个不同概念的融合可能会导致脆弱的代码,有时对任何一种数据都有效,有时则无效。如果人们没有明确说明某种接受``str``东西可以接受文本或二进制数据而不是一种特定的类型这也会导致API的混乱。这使情况变得复杂特别是对于任何支持多种语言的人来说因为API在声称支持文本数据时不会显式支持``unicode``。</p>
<p>为了使文本和二进制数据之间的区别更加清晰和明显Python 3做了大多数在互联网时代创建的语言所做的事情使文本和二进制数据成为不能盲目混合在一起的不同类型Python早于互联网的广泛使用。对于任何只处理文本或只处理二进制数据的代码这种分离并不构成问题。但是对于必须处理这两种数据的代码来说这确实意味着你现在可能必须关心你何时使用文本或二进制数据这就是为什么这不能完全自动化迁移。</p>
<p>首先,你需要决定哪些 API 接受文本,哪些接受二进制(由于保持代码工作的难度,<strong>强烈</strong> 建议你不要设计同时接受两种数据的 API如前所述这很难做得好。在 Python 2 中,这意味着要确保处理文本的 API 能够与 <code class="docutils literal notranslate"><span class="pre">unicode</span></code> 一起工作,而处理二进制数据的 API 能够与 Python 3 中的 <code class="docutils literal notranslate"><span class="pre">bytes</span></code> 类型一起工作(在 Python 2 中是 <code class="docutils literal notranslate"><span class="pre">str</span></code> 的一个子集,在 Python 2 中作为 <code class="docutils literal notranslate"><span class="pre">bytes</span></code> 类型的别名)。通常最大的问题是意识到哪些方法同时存在于 Python 2 和 3 的哪些类型上 (对于文本来说Python 2 中是 <code class="docutils literal notranslate"><span class="pre">unicode</span></code>Python 3 中是 <code class="docutils literal notranslate"><span class="pre">str</span></code>对于二进制来说Python 2 中是 <code class="docutils literal notranslate"><span class="pre">str</span></code>/<code class="docutils literal notranslate"><span class="pre">bytes</span></code>Python 3 中是``bytes``)。 下表列出了每个数据类型在 Python 2 和 3 中的 <strong>独特</strong> 的方法 (例如,<code class="docutils literal notranslate"><span class="pre">decode()</span></code> 方法在 Python 2 或 3 中的等价二进制数据类型上是可用的,但是它不能在 Python 2 和 3 之间被文本数据类型一致使用,因为 Python 3 中的 <code class="docutils literal notranslate"><span class="pre">str</span></code> 没有这个方法)。 请注意,从 Python 3.5 开始,<code class="docutils literal notranslate"><span class="pre">__mod__</span></code> 方法被添加到 bytes 类型中。</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 53%" />
<col style="width: 47%" />
</colgroup>
<tbody>
<tr class="row-odd"><td><p><strong>文本数据</strong></p></td>
<td><p><strong>二进制数据</strong></p></td>
</tr>
<tr class="row-even"><td><p></p></td>
<td><p>decode</p></td>
</tr>
<tr class="row-odd"><td><p>encode</p></td>
<td></td>
</tr>
<tr class="row-even"><td><p>format</p></td>
<td></td>
</tr>
<tr class="row-odd"><td><p>isdecimal</p></td>
<td></td>
</tr>
<tr class="row-even"><td><p>isnumeric</p></td>
<td></td>
</tr>
</tbody>
</table>
<p>通过在你的代码边缘对二进制数据和文本进行编码和解码,可以使这种区分更容易处理。这意味着,当你收到二进制数据的文本时,你应该立即对其进行解码。而如果你的代码需要将文本作为二进制数据发送,那么就尽可能晚地对其进行编码。这使得你的代码在内部只与文本打交道,从而不必再去跟踪你所处理的数据类型。</p>
<p>下一个问题是确保你知道你的代码中的字符串字头是代表文本还是二进制数据。你应该给任何呈现二进制数据的字面符号添加一个``b``前缀。对于文本,你应该给文本字面添加一个``u``前缀。(有一个 <a class="reference internal" href="../library/__future__.html#module-__future__" title="__future__: Future statement definitions"><code class="xref py py-mod docutils literal notranslate"><span class="pre">__future__</span></code></a> 导入来强制所有未指定的字头为Unicode但实际使用情况表明它并不像给所有字头显式添加一个``b``或``u``前缀那样有效)</p>
<p>作为这种二分法的一部分,你还需要小心打开文件。除非你一直在 Windows 上工作,否则你有可能在打开二进制文件时没有一直费心地添加``b``模式 (例如,用``rb``进行二进制读取)。 在 Python 3 下,二进制文件和文本文件显然是不同的,而且是相互不兼容的;详见 <a class="reference internal" href="../library/io.html#module-io" title="io: Core tools for working with streams."><code class="xref py py-mod docutils literal notranslate"><span class="pre">io</span></code></a> 模块。因此,你必须**决定**一个文件是用于二进制访问(允许读取和/或写入二进制数据)还是文本访问(允许读取和/或写入文本数据)。你还应该使用 <a class="reference internal" href="../library/io.html#io.open" title="io.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">io.open()</span></code></a> 来打开文件,而不是内置的 <a class="reference internal" href="../library/functions.html#open" title="open"><code class="xref py py-func docutils literal notranslate"><span class="pre">open()</span></code></a> 函数,因为 <a class="reference internal" href="../library/io.html#module-io" title="io: Core tools for working with streams."><code class="xref py py-mod docutils literal notranslate"><span class="pre">io</span></code></a> 模块从 Python 2 到 3 是一致的,而内置的 <a class="reference internal" href="../library/functions.html#open" title="open"><code class="xref py py-func docutils literal notranslate"><span class="pre">open()</span></code></a> 函数则不是 (在 Python 3 中它实际上是 <a class="reference internal" href="../library/io.html#io.open" title="io.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">io.open()</span></code></a>)。不要理会使用 <a class="reference internal" href="../library/codecs.html#codecs.open" title="codecs.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">codecs.open()</span></code></a> 的过时做法,因为这只是为了保持与 Python 2.5 的兼容性。</p>
<p>在Python 2和3中<code class="docutils literal notranslate"><span class="pre">str``和``bytes``的构造函数对相同的参数有不同的语义。在Python</span> <span class="pre">2中传递一个整数给``bytes</span></code>,你将得到整数的字符串表示:<code class="docutils literal notranslate"><span class="pre">bytes(3)</span> <span class="pre">==</span> <span class="pre">'3'</span></code>。但是在Python 3中一个整数参数传递给``bytes``将给你一个与指定的整数一样长的bytes对象其中充满了空字节<code class="docutils literal notranslate"><span class="pre">bytes(3)</span> <span class="pre">==</span> <span class="pre">b'\x00\x00\x00'</span></code>。当把bytes对象传给``str``时,类似的担心是必要的。 在Python 2中你只是又得到了该bytes对象<code class="docutils literal notranslate"><span class="pre">str(b'3')</span> <span class="pre">==</span> <span class="pre">b'3'</span></code>。但是在Python 3中你得到bytes对象的字符串表示<code class="docutils literal notranslate"><span class="pre">str(b'3')</span> <span class="pre">==</span> <span class="pre">&quot;b'3'&quot;</span></code></p>
<p>最后,二进制数据的索引需要仔细处理(切片 <strong>不需要</strong> 任何特殊处理)。在 Python 2 中 <code class="docutils literal notranslate"><span class="pre">b'123'[1]</span> <span class="pre">==</span> <span class="pre">b'2'</span></code>,而在 Python 3 中``b'123'[1] == 50``。 因为二进制数据只是二进制数的集合Python 3 会返回你索引的字节的整数值。 但是在 Python 2 中,因为 <code class="docutils literal notranslate"><span class="pre">bytes</span> <span class="pre">==</span> <span class="pre">str</span></code>,索引会返回一个单项的字节片断。 <a class="reference external" href="https://pypi.org/project/six">six</a> 项目有一个名为 <code class="docutils literal notranslate"><span class="pre">six.indexbytes()</span></code> 的函数,它将像在 Python 3 中一样返回一个整数: <code class="docutils literal notranslate"><span class="pre">six.indexbytes(b'123',</span> <span class="pre">1)</span></code></p>
<p>总结一下:</p>
<ol class="arabic simple">
<li><p>决定你的API中哪些采用文本哪些采用二进制数据</p></li>
<li><p>确保你对文本工作的代码也能对``unicode``工作对二进制数据的代码在Python 2中能对``bytes``工作(关于每种类型不能使用的方法,见上表)。</p></li>
<li><p>用``b``前缀标记所有二进制字词,用``u``前缀标记文本字词</p></li>
<li><p>尽快将二进制数据解码为文本,尽可能晚地将文本编码为二进制数据</p></li>
<li><p>使用 <a class="reference internal" href="../library/io.html#io.open" title="io.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">io.open()</span></code></a> 打开文件,并确保在适当时候指定 <code class="docutils literal notranslate"><span class="pre">b</span></code> 模式。</p></li>
<li><p>在对二进制数据进行索引时要小心</p></li>
</ol>
</section>
<section id="use-feature-detection-instead-of-version-detection">
<h4>使用特征检测而不是版本检测<a class="headerlink" href="#use-feature-detection-instead-of-version-detection" title="永久链接至标题"></a></h4>
<p>你不可避免地会有一些代码需要根据运行的 Python 版本来选择要做什么。做到这一点的最好方法是对你运行的 Python 版本是否支持你所需要的东西进行特征检测。如果由于某种原因这不起作用,那么你应该让版本检测针对 Python 2 而不是 Python 3。为了帮助解释这个问题让我们看一个例子。</p>
<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> 的一个功能该功能自Python 3.3开始在Python的标准库中提供并且通过PyPI上的 <a class="reference external" href="https://pypi.org/project/importlib2">importlib2</a> 提供给Python 2。你可能会想写代码来访问例如 <a class="reference internal" href="../library/importlib.html#module-importlib.abc" title="importlib.abc: Abstract base classes related to import"><code class="xref py py-mod docutils literal notranslate"><span class="pre">importlib.abc</span></code></a> 模块,方法如下:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version_info</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">importlib</span> <span class="kn">import</span> <span class="n">abc</span>
<span class="k">else</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">importlib2</span> <span class="kn">import</span> <span class="n">abc</span>
</pre></div>
</div>
<p>这段代码的问题是当Python 4出来的时候会发生什么最好是将Python 2作为例外情况而不是Python 3并假设未来的Python版本与Python 3的兼容性比Python 2更强:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version_info</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">importlib</span> <span class="kn">import</span> <span class="n">abc</span>
<span class="k">else</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">importlib2</span> <span class="kn">import</span> <span class="n">abc</span>
</pre></div>
</div>
<p>不过,最好的解决办法是根本不做版本检测,而是依靠特征检测。这就避免了任何潜在的版本检测错误的问题,并有助于保持你对未来的兼容:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">importlib</span> <span class="kn">import</span> <span class="n">abc</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">importlib2</span> <span class="kn">import</span> <span class="n">abc</span>
</pre></div>
</div>
</section>
</section>
<section id="prevent-compatibility-regressions">
<h3>防止兼容性退步<a class="headerlink" href="#prevent-compatibility-regressions" title="永久链接至标题"></a></h3>
<p>一旦你完全翻译了你的代码,使之与 Python 3 兼容,你将希望确保你的代码不会退步,不会在 Python 3上停止工作。如果你有一个依赖关系阻碍了你目前在Python 3上的实际运行那就更是如此了。</p>
<p>为了帮助保持兼容,你创建的任何新模块都应该在其顶部至少有以下代码块:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">division</span>
<span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">print_function</span>
</pre></div>
</div>
<p>你也可以在运行Python 2时使用``-3``标志,对你的代码在执行过程中引发的各种兼容性问题进行警告。如果你用``-Werror``把警告变成错误,那么你可以确保你不会意外地错过一个警告。</p>
<p>你也可以使用 <a class="reference external" href="https://pypi.org/project/pylint">Pylint</a> 项目和它的``--py3k``标志来提示你的代码,当你的代码开始偏离 Python 3 的兼容性时,就会收到警告。这也避免了你不得不定期在你的代码上运行 <a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a><a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a> 来捕捉兼容性的退步。这确实要求你只支持Python 2.7和Python 3.4或更新的版本因为这是Pylint支持的最小Python版本。</p>
</section>
<section id="check-which-dependencies-block-your-transition">
<h3>检查哪些依赖性会阻碍你的过渡<a class="headerlink" href="#check-which-dependencies-block-your-transition" title="永久链接至标题"></a></h3>
<p>在你使你的代码与 Python 3 兼容**之后**,你应该开始关心你的依赖关系是否也被移植了。 <a class="reference external" href="https://pypi.org/project/caniusepython3">caniusepython3</a> 项目的建立是为了帮助你确定哪些项目——直接或间接地——阻碍了你对Python 3的支持。它既有一个命令行工具也有一个在https://caniusepython3.com的网页界面。</p>
<p>该项目还提供了一些代码你可以将其集成到你的测试套件中这样当你不再有依赖关系阻碍你使用Python 3时你将有一个失败的测试。这使你不必手动检查你的依赖性并在你可以开始在Python 3上运行时迅速得到通知。</p>
</section>
<section id="update-your-setup-py-file-to-denote-python-3-compatibility">
<h3>更新你的``setup.py``文件以表示对Python 3的兼容<a class="headerlink" href="#update-your-setup-py-file-to-denote-python-3-compatibility" title="永久链接至标题"></a></h3>
<p>一旦你的代码在 Python 3 下工作,你应该更新你``setup.py``中的分类器,使其包含``Programming Language :: Python :: 3``并不指定单独的 Python 2 支持。这将告诉使用你的代码的人你支持Python 2 <strong></strong> 3。理想情况下你也希望为你现在支持的Python的每个主要/次要版本添加分类器。</p>
</section>
<section id="use-continuous-integration-to-stay-compatible">
<h3>使用持续集成以保持兼容<a class="headerlink" href="#use-continuous-integration-to-stay-compatible" title="永久链接至标题"></a></h3>
<p>一旦你能够完全在Python 3下运行你将希望确保你的代码总是在Python 2和3下工作。在多个Python解释器下运行测试的最好工具可能是 <a class="reference external" href="https://pypi.org/project/tox">tox</a> 。然后你可以将 tox 与你的持续集成系统集成,这样你就不会意外地破坏对 Python 2 或 3 的支持。</p>
<p>你可能还想在 Python 3 解释器中使用``-bb``标志以便在你将bytes与string或bytes与int进行比较时触发一个异常后者从 Python 3.5 开始可用)。默认情况下,类型不同的比较只是简单地返回``False``,但是如果你在文本/二进制数据处理或字节的索引分离中犯了一个错误,你就不容易发现这个错误。当这些类型的比较发生时,这个标志会触发一个异常,使错误更容易被发现。</p>
<p>基本上就是这样了! 在这一点上,你的代码库同时与 Python 2 和 3 兼容。你的测试也将被设置为不会意外地破坏 Python 2 或 3 的兼容性,无论你在开发时通常在哪个版本下运行测试。</p>
</section>
<section id="consider-using-optional-static-type-checking">
<h3>考虑使用可选的静态类型检查<a class="headerlink" href="#consider-using-optional-static-type-checking" title="永久链接至标题"></a></h3>
<p>另一个帮助移植你的代码的方法是在你的代码上使用静态类型检查器,如 <a class="reference external" href="http://mypy-lang.org/">mypy</a><a class="reference external" href="https://github.com/google/pytype">pytype</a>。这些工具可以用来分析你的代码就像它在Python 2下运行一样然后你可以第二次运行这个工具就像你的代码在Python 3下运行一样。通过像这样两次运行静态类型检查器你可以发现你是否错误地使用了二进制数据类型例如在Python的一个版本中与另一个版本相比。如果你在你的代码中添加了可选的类型提示你也可以明确说明你的API是使用文本数据还是二进制数据这有助于确保在两个版本的Python中所有的功能都符合预期。</p>
</section>
</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="#">将 Python 2 代码迁移到 Python 3</a><ul>
<li><a class="reference internal" href="#the-short-explanation">简要说明</a></li>
<li><a class="reference internal" href="#details">详情</a><ul>
<li><a class="reference internal" href="#drop-support-for-python-2-6-and-older">删除对Python 2.6及更早版本的支持</a></li>
<li><a class="reference internal" href="#make-sure-you-specify-the-proper-version-support-in-your-setup-py-file">确保你在你的``setup.py``文件中指定适当的版本支持</a></li>
<li><a class="reference internal" href="#have-good-test-coverage">良好的测试覆盖率</a></li>
<li><a class="reference internal" href="#learn-the-differences-between-python-2-3">了解Python 2 和 3之间的区别</a></li>
<li><a class="reference internal" href="#update-your-code">更新代码</a><ul>
<li><a class="reference internal" href="#division">除法</a></li>
<li><a class="reference internal" href="#text-versus-binary-data">文本与二进制数据</a></li>
<li><a class="reference internal" href="#use-feature-detection-instead-of-version-detection">使用特征检测而不是版本检测</a></li>
</ul>
</li>
<li><a class="reference internal" href="#prevent-compatibility-regressions">防止兼容性退步</a></li>
<li><a class="reference internal" href="#check-which-dependencies-block-your-transition">检查哪些依赖性会阻碍你的过渡</a></li>
<li><a class="reference internal" href="#update-your-setup-py-file-to-denote-python-3-compatibility">更新你的``setup.py``文件以表示对Python 3的兼容</a></li>
<li><a class="reference internal" href="#use-continuous-integration-to-stay-compatible">使用持续集成以保持兼容</a></li>
<li><a class="reference internal" href="#consider-using-optional-static-type-checking">考虑使用可选的静态类型检查</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>上一个主题</h4>
<p class="topless"><a href="index.html"
title="上一章">Python 指南</a></p>
<h4>下一个主题</h4>
<p class="topless"><a href="cporting.html"
title="下一章">将扩展模块移植到 Python 3</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/howto/pyporting.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="cporting.html" title="将扩展模块移植到 Python 3"
>下一页</a> |</li>
<li class="right" >
<a href="index.html" title="Python 指南"
>上一页</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>