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") 字符串格式化。

还有其他要养成的行和好习惯吗?

4

2 回答 2

12

微级别更改和 2to3 无法充分解决的最大问题是将默认字符串类型从字节更改为 Unicode。

如果您的代码需要对编码和字节 I/O 进行任何操作,则需要大量手动操作才能正确转换,以便必须为字节的内容保留字节,并在正确的阶段进行适当的解码。您会发现某些字符串方法(尤其是format())和库调用需要 Unicode 字符串,因此您可能需要额外的解码/编码周期才能将字符串用作 Unicode,即使它们实际上只是字节。

一些 Python 标准库模块在没有适当注意字节/unicode/编码问题的情况下使用 2to3 进行了粗略的转换,因此它们自己在什么字符串类型是合适的方面犯了错误,这无济于事。其中一些正在被淘汰,但至少从 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 应用程序中,并且所有重音或非拉丁字母都更改为错误的字母,从而造成无法阅读的混乱。(又名“mojibake”。)

还有“多字节”编码,通过使用多个字节来存储每个字符,尝试将更多字符放入可用空间。这些是为东亚地区引入的,因为有很多汉字。但也有 UTF-8,这是一种设计更好的现代多字节编码,可以容纳每个字符。

如果您正在处理多字节编码的字节字符串——今天您可能会这样做,因为 UTF-8 被广泛使用;实际上,在现代应用程序中不应该使用其他编码——那么除了跟踪正在使用的编码之外,您还有更多的问题。len()将告诉您以字节为单位的长度,而不是以字符为单位的长度,如果您开始索引和更改字节,您很可能会将多字节序列一分为二,生成无效序列并且通常会混淆所有内容。

出于这个原因,Python 1.6 及更高版本具有原生 Unicode 字符串(拼写为u'something'),其中字符串中的每个单元都是一个字符,而不是一个字节。您可以对len()它们进行切片、替换、正则表达式,它们将始终表现得恰到好处。对于文本处理任务,它们无疑更好,这就是 Python 3 将它们设为默认字符串类型的原因(无需u''.

问题在于,许多现有接口,例如 Windows 以外的操作系统上的文件名、HTTP 或 SMTP,主要是基于字节的,并以单独的方式指定编码。因此,当您处理需要字节的组件时,您必须注意将您的 unicode 字符串正确编码为字节,而在 Python 3 中,您必须在之前不需要的某些地方显式地执行此操作。

这是一个内部实现细节,Unicode 字符串在内部每个单元占用“两个字节”的存储空间。你永远看不到那个存储空间;你不应该从字节的角度来考虑它。无论 Python 选择如何在内存中表示它们,您正在处理的单元在概念上都是字符。

...在旁边:

这并不完全正确。在像 Windows 构建这样的 Python 的“狭义构建”上,Unicode 字符串的每个单元在技术上不是一个字符,而是一个 UTF-16 的“代码单元”。对于基本多语言平面中的字符,从 0x0000 到 0xFFFF,您不会注意到任何差异,但如果您使用的是 16 位范围之外的字符,即“星体平面”中的字符,您会发现它们采用两个单位而不是一个单位,而且,当你切片时,你再次冒着分裂角色的风险。

这是非常糟糕的,因为在 Unicode 超过 65,000 个字符的限制之前,Windows(和其他,如 Java)将 UTF-16 作为内存存储机制确定了这一点。但是,使用这些扩展字符仍然很少见,Windows 上的任何人都会习惯它们在许多应用程序中的使用,因此这对您来说可能并不重要。

在“广泛构建”中,Unicode 字符串由真正的字符“代码点”单元组成,因此即使是 BMP 之外的扩展字符也可以一致且轻松地处理。为此付出的代价是效率:每个字符串单元在内存中占用四个字节的存储空间。

于 2010-08-06T14:41:32.133 回答
5

我正在尝试养成使用诸如var1//var2每当我真正想要整数除法(而不是浮点数)之类的东西的习惯。向 Python 3 迈出的不是一大步,但至少我不必回去检查我所有的部门:)

于 2010-08-06T13:48:08.727 回答