原文: Why Python 3 exists (Or, the whole unicode/str/bytes thing was done for a reason)
这个月,我在PuPPy(普吉特海湾Python用户组)举行了一个 Q&A,这最终使得我解释了为什么Python 3应运而生,以及整个字符串/字节的处理过程。最终,我收到了一个对于这个解释的赞赏,这让我有点吃惊,因为我天真的以为人们都知道为什么创造Python 3。事后看来,假设大多数的人们,不管是Python的新手还是熟手,都已经被告知或由好奇心驱使去追寻发现关于这个问题的解释,是非常愚蠢的。因此,这篇博客目的在于简单的解释为什么Python 3存在,特别是为什么我们选择了完全向后兼容的unicode
/str
/bytes
修改,因为这是向Python 3移植代码真正棘手的部分。
快速看一下,下列文字语义上表示了什么?
'abcd'
如果你是Python 3用户,你可能会说,这是由字母"a", "b", "c"和"d"按顺序组成的字符串。
如果你是Python 2用户,你可能会说同样的话。你也有可能会说它代表了97, 98, 99, 和 100字节。这就是事实。在Python 2中有关于str
对象代表了什么的两种正确的解释,这就导致了修改语言,从而使得单独的Python 3答案是唯一的答案。
Python之禅说,“应该有一个 -- 最好是只有一个 -- 明显的方法来做到”。让语言中的文字可以代表文本数据或者二进制数据是一个问题。例如,如果你从网上读到一些东西,那你则必须非常小心辨别所返回的str
对象代表的是二进制数据还是文本数据,因为一旦这个对象离开了你的掌控,那么你就不可能知道了。或者在你的代码中,你打算将str
对象转换成文本数据 -- 或者其他什么的 -- 但你搞砸了,并且不小心跳过了那一步,此时可能会出现一个错误。由于str
对象可能代表了两个不同语义的类型,因此很难注意到这类型的上滑(slip-up)会在何时发生。
现在,你可能尝试并且辩解道,如果避免为文本数据使用str
类型,而是用unicode
取而代之,这些问题都可以Python 2中去解决。虽然这是完全正确的,但是实际上人们不会这样做。要么是人们想偷懒,所以不想劳神解码为Unicode,因为这需要额外的工作;要么人们对性能要求极高,并尽量避免解码的成本。无论是哪种,都假设你会好好编码以避免把事情搞得一团糟,然而,我们都知道,我们实际上是不完美的人类,都有可能犯错。若人们对于使用Python 2写出没有bug的代码的希望能够成真的话,那么我就不会不断的从基本每个将他们的代码移植到Python 3的人那儿听到,他们在他们的代码中发现了关于编码解码文本和二进制数据的潜在错误。
避免错误这一点是人们忘记的一个大问题。语言的简化和移除一个str对象可能代表的潜在含义会使得代码倾向于具备更少的错误。Python之禅指出,“显式胜于隐式”,是有原因的:歧义和隐性知识不是易于沟通的代码,它们容易出错,导致错误。通过迫使开发者明确地分离他们的二进制数据和文本数据,会导致某一类错误发生机率更低的更好的代码。
人们有时候会忘记,Python有多老了;Guido在1989年12月开始编写Python,在1991年2月作为开源代码第一次发布。这意味着Python自身早于1991年10月发布的Unicode标准第一卷。在几年间,Unicode标准化后创建的语言选择基于能够支持Unicode的编码来实现字符串。这使得Python 2位于这种不幸的位置,在这种情况下,它在2004年(Python 3计划开始的时候)获得了重要的关注, 但由于unicode
类型完全是可选的,并且人们不是对所有的文本数据使用此类型,它可以说是提供了对Unicode文本最薄弱的支持。
从任何一种书面语言支持Unicode和文本是很重要的。Python是世界的语言,而不仅仅是那些支持ASCII覆盖的罗马字母的语言。这就是为什么当涉及到文本时,Python 3使它成为"Unicode或者bust";它保证了所有的Python 3代码将支持世界上的所有人,无论开发者是否明确的编写代码指出。在Python 2中,那些花时间正确的为文本数据指定unicode类型的项目和不这么做的项目之间已经出现了分裂;而在Python 3中,并不存在这样的分裂,并且免费的支持所有的语言。
2004年,我们开始PEP 3100,从而开始设计Python 3 (旁白: PEP最初的编号是3000,但是我们重新将它编号为3100,这样编号为3000的PEP将是关于我们如何处理Python 3开发的PEP)。我们知道,Python的人气呈上升趋势,我们希望它的增长可以继续(令人欣慰的是,确实是这样的☺)。但是,这也意味着,如果我们要解决任何设计错误,以及帮助语言继续普及,我们需要现在来做,而不是等到以后。我们假设,如果我们没有把Python 3弄糟,一旦Python 2只用于旧版项目,而不是新版,Python 3将会比Python 2持续更长的时间和被使用更多,那么在一个足够长的时间段内会有更多的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版本的修正版本。这显然并未发生,而我们也吸取了教训。另外,我们并未看到这门语言的基本设计中有任何缺点以致于需要做如此大的变动。所以,希望Python 4不会做任何比可能从标准库中移除过时模块更强烈的动作了。
这就是为什么会是这样的。我们意识到,由于在Python 2中过度的使用str
类型,人们一直有一堆的问题,所以在Python 3中,我们通过明确地将文本数据从二进制数据中分隔开来来解决它们。另外,让所有的文本数据自动的支持Unicode,有助于项目更容易与多种语言一起工作。而当我们这样做的时候还,我们进行了更改,因为我们觉得越快越好。我们构造了过渡,认为社区将跟随我们留下Python 2,但是事实并非如此,取而代之的,我们已经花费了更多的时间,并使用一个Python 2/3兼容子集来管理过渡。