准备从Python 2.x迁移到3.x

11

众所周知(我希望如此),Python 3正在逐渐取代Python 2.x。当然,在大多数现有代码最终被移植之前,可能需要很多年,但现在我们可以在版本2.x的代码中做一些事情,以使转换更加容易。

显然,查看3.x的新特性将是有帮助的,但有哪些我们现在可以做的事情,以使即将到来的转换更加轻松(并使输出更新到并发版本更容易)? 我特别考虑了我们可以从脚本开头开始的行,使较早版本的Python更类似于3.x,尽管也欢迎其他习惯。

我能想到的最明显的要添加到脚本顶部的代码是:

from __future__ import division
from __future__ import print_function
try:
    range = xrange
except NameError:
    pass

我能想到最明显的习惯就是使用字符串格式化的方法 "{0} {1}!".format("Hello", "World")

还有其他好的编程习惯可以养成吗?

2个回答

12

微观层面的变化和2to3无法充分解决的最大问题是默认字符串类型从字节流变成了Unicode。

如果您的代码需要处理编码和字节I/O,它将需要大量手动转换,以确保必须是字节的内容仍然是字节,并且在正确的阶段得到适当的解码。您会发现一些字符串方法(特别是format())和库调用需要Unicode字符串,因此即使它们实际上只是字节,您也需要额外的解码/编码循环来将其用作Unicode字符串。

这并不是因为一些Python标准库模块已经使用2to3进行了粗略的转换而没有正确关注字节/Unicode/编码问题所帮助的,因此它们本身关于哪种字符串类型是适用的也存在错误。其中一些正在处理中,但至少从Python 3.0到3.2,您将在像urllib、email和wsgiref这样需要了解字节编码的软件包中面临困惑和潜在的错误行为。

您可以通过小心地编写每个字符串文字来缓解问题。对于任何本质上基于字符的内容,请使用u''字符串;对于任何真正的字节,请使用b''字符串;对于“默认字符串”类型,在这种情况下并不重要或者您需要匹配库调用的字符串使用要求,使用''

不幸的是,b''语法只在Python 2.6中引入,因此这样做会割裂早期版本的用户。

注:

它们之间有什么区别?

哦,天啊。嗯...

一个字节包含0-255范围内的值,并且可以表示大量二进制数据(例如图像内容)或一些文本,在这种情况下必须选择一种标准来将字符集映射到这些字节中。大多数这些“编码”标准都以相同的方式将正常的“ASCII”字符集映射到字节0-127中,因此在Python 2中仅处理ASCII文本时使用字节流通常是安全的。

如果您想在字节字符串中使用ASCII字符集之外的任何字符,则会遇到麻烦,因为每种编码都将不同的字符映射为剩余的128-255个字节值,并且大多数编码无法将每个可能的字符映射到字节。这就是为什么当您从一个语言环境加载文件到另一个语言环境的Windows应用程序中时,所有带重音符号或非拉丁字母的字母都会更改为错误的字母,从而产生一个难以阅读的混乱(也称为“乱码”)。
还有“多字节”编码,它们尝试通过使用多个字节来存储每个字符来适合更多的字符。这些编码是为东亚语言环境引入的,因为有非常多的汉字。但是还有UTF-8,这是一种设计更好的现代多字节编码,可以容纳每个字符。
如果您正在使用多字节编码的字节字符串(今天您可能会这样做,因为UTF-8被广泛使用;真正的现代应用程序不应使用其他编码),那么您会遇到更多问题,而不仅仅是跟踪您正在使用的编码。 len() 将告诉您以字节为单位的长度,而不是以字符为单位的长度,如果您开始索引和更改字节,则很可能将多字节序列分成两个部分,生成一个无效的序列并通常使一切变得混乱。
出于这个原因,Python 1.6及更高版本具有本机Unicode字符串(拼写为 u'something'),其中字符串中的每个单位都是一个字符,而不是一个字节。您可以对它们执行 len()、slice、replace、regex 等操作,并且它们总是表现得正确。对于文本处理任务,它们无疑更好,这就是为什么 Python 3 将它们作为默认字符串类型(无需在 '' 前加上 u)。问题在于许多现有的接口(如非Windows操作系统上的文件名、HTTP或SMTP)基本上是基于字节的,并且具有单独的指定编码方式。因此,当你处理需要字节的组件时,你必须注意正确地将 Unicode 字符串编码为字节,在 Python 3 中,你将不得不在一些之前不需要的地方明确地进行编码。 Unicode 字符串每个单位内部占用2个字节的存储空间是一种内部实现细节,你无法看到该存储空间;你不应以字节为单位来考虑它。你正在使用的单位是概念上的字符,无论 Python 如何选择在内存中表示它们。
...附言:
这并不完全正确。在 Windows 等“窄版本”的 Python 上,每个 Unicode 字符串单位不是技术上的字符,而是 UTF-16 的“代码单元”。对于 Basic Multilingual Plane 中的字符,从0x0000-0xFFFF,你不会注意到任何差异,但如果你使用位于这个 16 位范围之外的字符,即“星际平面”中的字符,则会发现它们需要两个单位而不是一个单位,同时,你会冒着在切片时分割字符的风险。
这相当糟糕,并且之所以会发生这种情况,是因为 Windows(和其他一些,例如 Java)在 Unicode 超过65,000个字符的限制之前,已经确定了使用 UTF-16 作为内存中的存储机制。然而,对于这些扩展字符的使用仍然相当罕见,任何在Windows上的人都会习惯于它们在许多应用程序中被破坏,因此对于你来说可能并不重要。
在“宽版本”中,Unicode 字符串由真正的字符“代码点”单位组成,因此甚至 BMP 之外的扩展字符也可以得到一致且易于处理的处理。为此需要付出的代价是效率:每个字符串单位在内存中占用4个字节的存储空间。

说到字节和Unicode字符串,它们有什么区别?只是字节字符串每个字符使用一个字节,而Unicode使用两个字节吗? - Wayne Werner
“UTF-8……在现代应用程序中不应使用其他编码”,除非您的政府规定使用其他编码,例如gb18030 :-) - John Machin
那是一篇非常出色的关于字节串和Unicode之间区别的解释。我对ASCII和Unicode有些了解,但显然我不熟悉Python(尤其是3.x版本)如何处理它们。我希望我能够投多次赞成票 ;) - Wayne Werner
这并没有得到缓解的事实是,一些Python标准库模块已经使用2to3进行了粗略转换,而没有适当地关注字节/Unicode/编码问题 - 这仍然是一个问题吗? - gerrit

5

我正在尝试养成这样的习惯,每当我需要整数除法(而不是浮点数)时,使用var1//var2这样的表达式。虽然这并不是迈向Python 3的重大步伐,但至少我不必返回检查所有的除法了 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接