如何将Python2代码移植到Python3

来自菜鸟教程
跳转至:导航、​搜索

介绍

Python 于 1980 年代后期开发,并于 1991 年首次发布。 Python 的名字灵感来自英国喜剧团体 Monty Python,它被认为是命令式通用 ABC 编程语言的继承者。 在其第一次迭代中,Python 已经包含了异常处理、 函数 和具有继承 类。

本教程将指导您了解将代码从 Python 2 迁移到 Python 3 时的最佳实践和注意事项,以及您是否应该维护与这两个版本兼容的代码。

背景

Python 2 于 2000 年发布,标志着一个更加透明和包容的语言开发过程。 它包括更多的程序化特性,并在整个开发过程中添加了更多特性。

Python 3 被视为 Python 的未来,是目前正在开发的语言版本。 Python 3 于 2008 年底发布,解决并修正了内在设计缺陷。 但是,由于该语言不向后兼容 Python 2,因此 Python 3 的采用速度很慢。

Python 2.7 于 2010 年作为 2.x 版本的最后一个发布。 Python 2.7 背后的意图是通过提供两者之间的某种兼容性度量,使 Python 2.x 用户更容易将功能移植到 Python 3。

您可以通过阅读我们的教程“Python 2 与 Python 3:实用注意事项”来了解有关 Python 版本和选择使用哪个版本的更多信息。

从 Python 2.7 开始

要迁移到 Python 3,或同时支持 Python 2 和 Python 3,您应该确保您的 Python 2 代码与 Python 2.7 完全兼容。

许多开发人员已经专门使用 Python 2.7 代码,但重要的是要确认仅受早期版本支持的任何内容都可以在 Python 2.7 上正常工作并且与 Python 2.7 风格一致。

确保您的代码在 Python 2.7 中尤为重要,因为它是唯一仍在维护并接收错误修复的 Python 2 版本。 如果您正在使用 Python 2 的早期版本,您将不得不解决您遇到的代码不再受支持且不再接收 buxfixes 的问题。

此外,低于 2.7 的 Python 版本不支持某些使您更容易移植代码的工具,例如查找编程错误的 Pylint 包。

重要的是要记住,尽管 Python 2.7 目前仍在得到支持和维护,但它最终会走到生命的尽头。 PEP 373 详细介绍了 Python 2.7 的发布时间表,并在撰写本文时将其日落日期标记为 2020 年。

测试覆盖率

创建测试用例可能是将 Python 2 代码迁移到 Python 3 代码的重要部分。 如果您维护多个版本的 Python,您还应该确保您的测试套件具有良好的整体覆盖率,以确保每个版本仍然按预期工作。

作为测试的一部分,您可以将交互式 Python 案例添加到所有函数、方法、类和模块的文档字符串中,然后使用内置的 doctest 模块 来验证它们是否如图所示工作。

除了 doctest,您还可以使用 coverage.py 包 来跟踪单元测试覆盖率。 该工具将监视您的程序并注意哪些部分代码已被执行,哪些部分可能已被执行但没有被执行。 coverage.py 可以将报告打印到命令行或提供 HTML 输出。 它通常用于衡量测试的有效性,向您展示代码的哪些部分正在通过测试进行,哪些没有。

请记住,您的目标不是 100% 的测试覆盖率——您希望确保覆盖任何令人困惑或不寻常的代码。 对于最佳实践,您应该以 80% c 超额为目标。

了解 Python 2 和 Python 3 之间的差异

了解 Python 2 和 Python 3 之间的差异将确保您能够利用 Python 3 中可用或将可用的新功能。

我们关于“Python 2 与 Python 3”的指南介绍了两个版本之间的一些 关键差异 ,您可以查看 官方 Python 文档 了解更多信息细节。

在开始移植和迁移时,您现在可以实现一些语法更改。

print

print Python 2 的语句已更改为 print() Python 3 中的函数。

蟒蛇2 蟒蛇 3
print "Hello, World!" print("Hello, World!")

exec

exec Python 2 的语句已更改为允许 Python 3 中显式局部变量和全局变量的函数。

蟒蛇2 蟒蛇 3
exec code exec(code)
exec code in globals exec(code, globals)
exec code in (globals, locals) exec(code, globals, locals)

///

Python 2 使用 / 运算符,Python 3 介绍 // 用于楼层划分。

蟒蛇2 蟒蛇 3
5 / 2 = 2 5 / 2 = 2.5
5 // 2 = 2

要在 Python 2 中使用这些运算符,请从 __future__ 模块中导入 how-to-import-modules-in-python-3 division

from __future__ import division

阅读更多关于 整数除法

raise

在 Python 3 中,使用参数引发异常需要括号,并且 strings 不能用作异常。

蟒蛇2 蟒蛇 3
raise Exception, args raise Exception
raise Exception(args)
raise Exception, args, traceback raise Exception(args).with_traceback(traceback)
raise "Error" raise Exception("Error")

except

在 Python 2 中很难列出多个异常,但在 Python 3 中发生了变化。

注意 as 明确使用 except 在 Python 3 中

蟒蛇2 蟒蛇 3
except Exception, variable: except AnException as variable:
except (OneException, TwoException) as variable:

def

在 Python 2 中,函数可以采用 tupleslists 之类的序列。 在 Python 3 中,这种解包已被删除。

蟒蛇2 蟒蛇 3
def function(arg1, (x, y)): def function(arg1, x_y): x, y = x_y

expr

Python 2 的反引号语法不再存在。 采用 repr() 或者 str.format() 在 Python 3 中。

蟒蛇2 蟒蛇 3
x = `355/113` x = repr(355/113):

字符串格式

字符串格式化语法已从 Python 2 更改为 Python 3。

蟒蛇2 蟒蛇 3
"%d %s" % (i, s) "{} {}".format(i, s)
"%d/%d=%f" % (355, 113, 355/113) "{:d}/{:d}={:f}".format(355, 113, 355/113)

学习 如何在 Python 3 中使用字符串格式化程序。

class

没有必要说明 object 在 Python 3 中。

蟒蛇2

class MyClass(object):
    pass

蟒蛇 3

class MyClass:
    pass

在 Python 3 中,元类设置为 metaclass 关键词。

蟒蛇2

class MyClass:
    __metaclass__ = MyMeta
class MyClass(MyBase):
    __metaclass__ = MyMeta

蟒蛇 3

class MyClass(metaclass=type):
    pass
class MyClass(MyBase, metaclass=MyMeta): 
    pass

更新代码

您可以使用两个主要工具将代码自动更新到 Python 3,同时使其与 Python 2 兼容:futuremodernize。 这些工具中的每一个都有不同的行为:future 致力于使 Python 3 习语和最佳实践存在于 Python 2 中,而 modernize 旨在使用 Python 的 Python 2/3 子集 [ X204X]六个模块提高兼容性。

使用这些工具来处理重写代码的细节可以帮助您识别和纠正潜在的问题和歧义。

您可以在您的单元测试套件上运行该工具,以直观地检查和验证代码,并确保所做的自动修订是准确的。 一旦测试通过,您就可以转换您的代码。

从这里开始,您可能需要进行一些手动修改,特别是针对上面部分 中提到的 Python 2 和 3 之间的 更改。

利用 future,您应该考虑将这个 import 语句添加到每个 Python 2.7 模块中:

from __future__ import print_function, division, absolute_imports, unicode_literals

虽然这也会导致重写,但它将确保您的 Python 2 代码与 Python 3 语法保持一致。

最后,您可以使用 pylint 包 来识别代码中的任何其他潜在问题。 该软件包包含数百个单独的规则,涵盖了可能发生的广泛问题,包括 PEP 8 样式指南 规则以及使用错误。

您可能会发现您的代码中有一些结构可能会混淆 pylint 和用于自动迁移的工具。 如果您不能简化这些构造,则需要使用彻底的单元测试用例。

持续集成

如果您要为多个版本的 Python 维护代码,则需要在开发时尽可能频繁地通过持续集成(而不是手动)在代码上运行和重新运行单元测试套件.

如果您使用 six 包 作为 Python 2 和 3 兼容性维护的一部分,则需要使用多个环境进行测试。

您可以考虑使用的一种环境管理工具是 tox 包,因为它会检查您的包安装有不同的 Python 版本,在您的每个环境中运行测试,并充当持续集成服务器的前端。

结论

重要的是要记住,随着更多开发人员和社区的注意力集中在 Python 3 上,该语言将变得更加精炼并符合程序员不断变化的需求,而对 Python 2.7 的支持将越来越少。 如果您决定同时维护 Python 2 和 Python 3 的代码库版本,则使用前者的难度可能会越来越大,因为随着时间的推移,它会收到更少的错误修复。

值得关注将 Python 2 移植到 Python 3 的项目,包括案例研究,例如 Porting chardet to Python 3