Python 2 和 Python 3,一直是一个话题。Brett Cannon 是 Python 的核心开发者,听听他关于 Python3 的看法。

本月我在 Puppy 上开展了一个问答活动(Puget Sound Python用户组),让我最终解释了为什么 Python 3 会出现以及整个的 string/bytes 处理机制。我因此受到赞扬这让我很惊讶,因为原本我天真的认为大家都清楚为什么 Python 3 会出现。回想起来我真是太傻,竟然认为大部分人(不管是刚接触 Python 或者已经有一段时间)应该要么已经被告知要么有好奇心驱使去搜寻获取一个解释。所以这篇博文打算简单的解释一下为什么 Python 3 会存在,特别是我们为什么选择打破整个向后兼容 unicode/str/bytes,因为这使得代码移植到 Python 3 非常棘手。

在 Python 2 中文本和二进制数据是一个烂摊子

快,下面的文字代表什么语义?

1 \’abcd\’

如果你是 Python 3 的用户,你会说它是一个按照 “a”、”b”、”c”、”d”顺序包含四个字母的字符串。

如果你是 Python 2 的用户,你也会说同样的话。你同样也可能会说它是代表 97、98、99、100 的数组。事实上在 Python 2中对于 str 代表的含义有两种正确的回答,这改变了语言的初衷。所以在 Python 3 中答案是唯一的。

《Python之禅》里讲“应该有一个“应该有一个––最好只有一个显而易见的方式去实现”。文字既能代表文本数据又能代表二进制数据这很麻烦。一旦对象 脱离你的控制,你将无从知晓。比如,如果你从网络上读到一些东西,你得小心判别这个 str 是代表二进制数据还是文本数据。或者你的代码里有一个bug,它是用来将 str 变换成文本信息––或者完全别的东西––但你搞砸了,不小心跳过了这一步。由于 str 潜在的代表两种类型,此类错误发生的时候,我们很难意识到。

也许你会说这些问题在 Python 2 中完全可以解决,只要你用 Unicode 而不是 str 去代表文本。虽然说的很对,但现实中人们不会那样去做。人们要么很懒,试着去避免解码到 Unicode 带来的额外工作,要么对性能要求很高,不想因为解码带来额外的损失。无论哪种情况,它都假设你代码足够好而避免了那些麻烦,但我们都清楚人不是完美的都会犯错。如果人们寄希望于在 Python 2 中写出没有 bug 的代码能够成真,我也不会总是听到几乎每个将他们的工程转向 Python 3 的人讲他们在代码中间文本和二进制数据编解码的潜在 bug。

避免 bug 是一件很重要的事情,但却经常被人遗忘。简化语言以及移除 str 可能代表的模糊含义会减少代码的出错率。《Python 之禅》指出“显胜于隐”的一个原因是: 歧义和隐性知识使得代码不容易沟通,进而容易出错,导致 bug。通过迫使开发者明确地分离出他们的二进制数据和文本数据,会使代码减少某一类的 bug。

其他语言都已经完全支持 Unicode(出于好的初衷)

人们有时会忘记 Python 有多久的历史了:Guido 在1989年12月开始编写 Python,在1991年2月开源发布。这意味着它早于在1991年10月发布的第一版  Unicode 标准。在接下来几年中,晚于 Unicode 标准出现的语言都选择在支持 Unicode 编码的基础上实现自己的 str 类型。这让 Python 2 处于尴尬的境地。直到2004年开始引发关注(那时 Python 3 开始酝酿),但它争辩说对于Unicode文本的弱支持是基于unicode 完全可选 ,人们不会在所有的文本信息上使用它。

支持 Unicode 和来自任何语言的的文本是非常重要的。Python 是一门面向世界的语言,而不只是那些支持 ASCII 码覆盖的罗马数字的语言。这就是为什么 Python 3 在处理文本时选择“要么使用 Unicode,要么就干脆不支持”的原因。官方宣称所有的 Python 3 代码都将支持世界上的每一个人,无论写代码的开发者是否有着明确的意图。在 Python 2 中,按照能够正确支持 Unicode 类型文本信息将项目分为两类;在 Python 3 中则不存在此类问题,并且它支持所有语言。

我们设想 Python 将更越来越受欢迎

在2004年我们开始编写 PEP 3000 进而开始设计 Python 3 (注:PEP 最初编号为 3000,但我们把它重新编号为 3100,这样我们就可以借助 编号 3000 的 PEP 来开发 Python 3)。我们清楚 Python 的受欢迎程度在不断上升,我们也希望能够延续这种势头(谢天谢地,目前如此)。但这也同时意味着如果我们想及时修正所有的设计缺陷来保证它的受欢迎度,最好趁现在而不是以后。我们设想 Python 3 相较于 Python 2 会持续更长一段时间,而 Python 2.7 仅仅是用来维护以前遗留的项目,而不会用于新项目中,那用 Python 3 写出的代码一定会多于用 Python 2 的。所以我们决定来承受由 Python 2 向 3 转型之痛。并且在这种设想下开发了 Python 3。想要知道 Python 3 的代码量能否赶超 Python 2,很显然需要数十年时间。

我们再也不会做这种打破向后兼容性的事情

我们团队已经下定决心再也不会让诸如 unicode/str/bytes 这样重大的改变随意发生。当我们决心开始 Python 3 的时候,我们设想同时希望社区能够像 Python 做的那般并且发布支持 Python 2的最终版本,接着切换到 Python 3 的功能开发中,只对 Python 2 做bug修复版本。这显然没有发生,同时也给我们上了一课。加上我们也没有发现在语言的基础设计中间存在需要做出如此重大改变来满足需要的缺陷。也希望 Python 4 不要发生比从标准库中移除废弃模块更激烈的改变。

结论

这就是 Python 3 为什么是这个样子的原因。我们意识到很多 bug 都是由于在 Python 2 中对 str 类型超负荷的使用。所以我们在 Python 3 中通过将文本数据和二进制数据分离来修正它。它也促使所有的文本信息都支持 Unicode,从而使得工程更容易在多种语言下工作。我们做出了改变,因为我们明白这种改变越早越好。我们在转型期曾经认为社区最终会摒弃 Python 2 和我们一道,但结局并非如此,相反我们花费了更多时间并且用一个 Python 2/3 的兼容子集来实现这种过渡。