0
点赞
收藏
分享

微信扫一扫

python Python 3 和 Python 2 的主要差异

前面已经说过,Python 3 打破了对 Python 2 的向后兼容。但它并不是完全重新设计的。 而且,也并不是说 2.x 版本的 Python 模块在 Python 3 下都无法运行。代码可以完全跨版本兼 容,无需其他工具或技术在两大版本上都可以运行,但一般只有简单应用才能做到这一点。

1.5.1 为什么要关注这些差异

本章前面说过我个人对 Python 2 兼容性的看法,但是目前不可能完全忽视这一点。还有 一些 Python 包(例如第 6 章将讲到的 fabric)十分实用,但可能短期内不会迁移到 Python 3。

另外,有时我们还会受到所在公司的制约。现有的遗留代码可能非常复杂,迁移代码 的费用难以承受。所以即使我们现在决定只用 Python 3,短期内也不可能完全放弃 Python 2。 如今想要自称专业开发者,没有对社区的回馈是说不过去的,所以帮助开源软件开发 者向现有软件包中添加对 Python 3 的兼容,可以很好地偿还在使用这些软件包时产生的“道 德债(moral debt)”。当然,不了解 Python 2 和 Python 3 的差异是无法做到这一点的。顺便

提一下,对于 Python 3 新手来说,这也是一项很好的练习。 1.5.2 主要的语法差异和常见陷阱

要比较不同版本之间的差异,最好的参考资料就是 Python 文档。不过为了方便读者, 本节总结了其中最重要的内容。但不熟悉 Python 3 的读者还是要去阅读官方文档。

Python 3 引入的重要差异一般可分为以下几个方面。

• 语法变化,删除/修改了一些语法元素,并添加了一些新的语法元素。 • 标准库中的变化。

• 数据类型与集合的变化。

1.语法变化

有些语法变化会导致当前代码无法运行,这些变化是最容易发现的,它们会导致代码 根本无法运行。包含新语法元素的 Python 3 代码在 Python 2 中无法运行,反之亦然。由于 删除了某些元素,导致 Python 2 代码显然无法与 Python 3 兼容。运行有这些问题的代码时, 解释器很快就会抛出 SyntaxError 异常。下面是一个无法运行的脚本示例,只包含两个

1.5 Python 3和Python 2的主要差异 5

语句,都会引发语法错误而无法运行:

      print("hello world")

      print "goodbye python2"

上述代码在 Python 3 中的实际运行结果如下:

   $ python3 script.py

     File "script.py", line 2

       print "goodbye python2"

                            ^

   SyntaxError: Missing parentheses in call to 'print'

列出所有的语法差异会比较长,而且 Python 3.x 的新版本也会不时添加新的语法元素, 在较早版本的Python中就会引发错误(即使在相同的3.x版本上也会报错)。其中最重要的 语法差异将会在第 2 章和第 3 章中讲到,所以这里无需全部列出。

与 Python 2.7 相比,删除或改动的内容要相对少一些,下面给出最重要的变化内容。

• print不再是一条语句而是一个函数,所以必须加上括号。

• 捕获异常的语法由 except exc, var 改为 except exc as var。

• 弃用比较运算符<>,改用!=。

• frommoduleimport*(https://docs.python.org/3.0/reference/simple_stmts.html#import) 现在只能用于模块,不能用在函数中。

• 现在 from .[module] import name 是相对导入的唯一正确的语法。所有不以 点字符开头的导入都被当作绝对导入。

• sorted函数与列表的sort方法不再接受cmp参数,应该用key参数来代替。

• 整数除法表达式(如 1/2)返回的是浮点数。取整运算可以用//运算符,如 1//2。

这样做的好处是浮点数也可以用这个运算符,所以 5.0//2.0 == 2.0。 2.标准库中的变化

语法变化很容易发现,标准库中的重大变化也是非常容易发现的。Python 的每个后续 版本都会向标准库模块中添加、弃用、改进或完全删除某些内容。在旧版 Python(1.x 和 2.x)中也会定期有这样的变化,所以出现在 Python 3 中并不让人吃惊。大多数情况下,对 于删除或重组的模块(例如 urlparse 移到了 urllib.parse),在运行解释器时会对导 入语句抛出异常。这样的问题很容易发现。无论如何,为了确保能够发现所有类似的问题, 完整的代码测试覆盖率是必不可少的。在某些情况下(例如使用延迟加载模块时),这个通 常在全局导入时出现的问题并不会出现,直到在代码中将某些模块作为函数调用时才会出 现。因此,在测试期间确保每行代码都要实际运行是很重要的。


6 第1章 Python现状

延迟加载模块

延迟加载模块是指在全局导入时并不加载的模块。在 Python 中,import 语句可以包含在函数内部,这样导 入是在函数调用时才会发生,而不是在全局导入时发 生。在某些情况下,模块的这种加载方式可能比较合理, 但大多数情况下,这只是对设计不佳的模块结构的变通 方法(例如避免循环导入),通常应避免这种加载方式。 当然,对于标准库模块来说,没有理由使用延迟加载。

3.数据类型与集合的变化

开发人员在努力保持兼容性或只是将现有代码迁移到 Python 3 上时,需要特别注意 Python 中数据类型与集合的表示方式的变化。虽然不兼容的语法变化或标准库变化很容易 发现,也很容易修复,但集合与数据类型的变化要么难以察觉,要么需要大量的重复工作。 这样的变化列表会很长,再次重申,官方文档是最好的参考资料。

不过,这一节必须讲一下 Python 3 中字符串处理方式的变化,因为这是 Python 3 中最 具争议也是讨论最多的变化,尽管这是一件好事,使很多问题变得更加明确。

现在所有字符串都是 Unicode,字节(bytes)需要加一个 b 或 B 的前缀。Python 3.0 和 3.1 不支持使用 u 前缀(例如 u"foo"),使用的话会引发语法错误。不支持这个前缀是 引发所有争议的主要原因。这导致难以编写能够兼容 Python 不同分支的代码,2.x 版需要 用这个前缀来创建 Unicode。Python 3.3 又恢复了这个前缀,虽然没有任何语法上的意义, 只是为了简化兼容过程。

1.5.3 用于保持跨版本兼容性的常用工具和技术

在 Python 不同版本之间保持兼容性是一项挑战。根据项目的大小不同,这项挑战可能 会增加许多额外的工作量,但绝对可行,也很值得去做。对于在许多环境中都会用到的 Python 包来说,必须要保持跨版本兼容性。如果开源包没有定义明确并经过测试的兼容范 围(compatibility bound),是不太可能流行起来的。而且,对于只在公司网络封闭使用的第 三方代码来说,也可以大大受益于在不同环境中的测试。

这里应该注意,虽然这一部分内容主要关注 Python 不同版本之间的兼容,但这些方法 也适用于保持与外部依赖项之间的兼容,外部依赖项包括不同的包版本、二进制库、系统 或外部服务等。

           整个过程主要分为 3 个部分,按重要性排序如下。


1.5 Python 3和Python 2的主要差异 7

• 定义并记录目标兼容范围的及其管理方法。

• 在每个环境中进行测试,并对每个兼容的依赖版本进行测试。

• 实现实际的兼容代码。 告知兼容范围是整个过程中最重要的一部分,因为这可以让代码使用者(开发人员)

对代码的工作原理和未来的变化方式有一定的预期和假设。我们的代码可能用于多个不同 项目的依赖,这些项目也在努力管理兼容性,所以把代码兼容性说清楚还是很重要的。

本书总是尽量给出几个选择,而不会强烈推荐某个特定选项,而这里是少数几个例外之一。 目前来看,管理兼容性未来变化的最佳方法,就是正确使用语义化版本(Semantic Versioning semver)的版本号。它是一个广为接受的标准,用仅包含 3 个数字的版本标识符来标记代码的 变化范围。它还给出了如何处理弃用的方法建议。下面是摘录 semver 官网的摘要。

版本格式:主版本号.次版本号.修订号,版本号递增规则如下。

• 主版本号(MAJOR):当你做了不兼容的 API 修改。

• 次版本号(MINOR):当你做了向后兼容的功能性新增。

• 修订号(PATCH):当你做了向后兼容的问题修正。 先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。 测试时就会发现一个悲伤的事实,为了保证代码与每个依赖版本和每个环境(这里环

境指的是 Python 版本)都保持兼容,必须在所有可能的组合中对代码进行测试。当然,如 果项目的依赖很多,做到这一点基本是不可能的,因为随着依赖版本数目的增加,组合的 数目也会迅速增加。因此,通常需要做一些权衡,使得运行所有兼容性测试无需花费数年 的时间。第 10 章中介绍一般的测试,里面也介绍了所谓的矩阵测试中工具的选择。

项目遵循 semver 的好处在于,通常只有主版本才需要 测试,因为次版本和修订版本中保证没有向后不兼容的 变化。只有项目不违背这样的约定,这种说法才能成立。 不幸的是,每个人都会犯错,许多项目中都出现了后向 不兼容的变化,甚至在修订版本中也出现了这种变化。 尽管如此,由于 semver 声称对次版本和修订版本的变 化保持严格的向后兼容,那么打破这个规则就可以视为 bug,可以在修订版本中进行修复。

 如果明确定义了兼容范围并严格测试,那么实现兼容层就是最后一步,也是最不重要

的一步。但是,每一位对这个话题感兴趣的程序员都应该知道下列工具和技术。

举报

相关推荐

0 条评论