C.2 都发生了哪些变化
Python 3的变化并不是令人难以置信的,它并非变得让你不再认识Python。本附录提供了一些主要变化的概述:
print变成了print();
默认情况下字符串会转换为Unicode编码;
增加了一个单类(single class)类型;
更新了异常的语法;
更新了整数;
迭代无处不在。
C.2.1 print变成了print()
到print()的转变是打破了最大数量的现存Python代码的一个变化。Python中为什么要将其从一条语句变化成一个内置函数(BIF)呢?因为将print作为声明会在很多方面受到限制,正如Guido在他的“Python遗憾”(Python Regrets)谈话中所详述的,他列举了认为是这门语言缺点的方方面面。此外,print作为一条语句将限制对它的改进。然而,当print()可用做一个函数时,就可以添加新的关键字参数,能够利用关键字参数覆写某些标准行为,并且也可以根据需要来替代print(),就像任何其他的内置函数一样。下面是Python改进前后的对比示例。
Python 2.x
>>> i = 1
>>> print 'Python' 'is', 'number', i
Pythonis number 1
Python 3.x
>>> i = 1
>>> print('Python' 'is', 'number', i)
Pythonis number 1
在上面的例子中,我们故意遗漏了Python和is之间的逗号,这样做是为了展示字符串字面里连接并没有改变。可以在“Python 3.0中的新内容“(What’s New in Python 3.0)”文档(可以参阅 C.3 节)中查看更多示例。此外,可以在 PEP 3105 找到关于该变化的更多信息。
C.2.2 字符串:默认为Unicode编码
目前Python用户面对的又一个“陷阱”就是,字符串现在默认为Unicode编码。这种变化不可能很快就来,当处理Unicode和通常的ASCII字符串时,无数的Python开发人员遇到这种问题已经不止一两天了。这种问题看起来如下所示。
UnicodeEncodeError: 'ascii' codec can't encode character u'\xae' in position 0: ordinal not in range(128)
在Python 3.x中这种类型的问题将不再经常发生。关于Python中使用Unicode的更多信息,可以查看 Unicode HOWTO 文档(请参阅 C.3 节的 Web 地址)。随着新版本的Python采用了这种模型,用户将不再需要使用 Unicode和ASCII/非Unicode字符串这些术语。“Python 3.0中的新内容”(What’s New in Python 3.0)文档相当详细地总结了这种新模型。
Python 3使用了文本(text)和(二进制)数据的概念,而非Unicode字符串和8位字符串。所有的文本都是Unicode编码的。然而,编码的Unicode表示成二进制数据。用来保存文本的类型是str,而用来保存数据的类型是bytes。
关于语法,因为现在默认的是Unicode编码,所以前导u或U已经弃用。同样地,新的字节对象需要为它的字面里(可以在PEP 3112找到更多信息)提供一个b或B前置。
表C-1比较了各种字符串类型,并显示了它们从版本2.x到 3.x如何改变。表C-1还包括一个新的可变字节数组(mutable bytearray)类型。
表C-1 Python 2和Python 3中的字符串
C.2.3 单类类型
在Python 2.2之前,Python的对象不像其他语言中的类:类是“类”对象,而实例是“实例”对象。这与人们普遍理解的内容形成鲜明对比:类是类型,而实例是类的对象。由于这种“缺陷”,你不能继承数据类型以及修改它们。在Python 2.2中,核心开发团队提出了一种新型的类,这种类表现起来更像人们所期望的那样。此外,这种变化意味着常规的Python类型可以继承了,在Guido的“Python 2.2中的统一类型和类”(Unifying Types and Classes in Python 2.2)文章中描述了这一变化。然而,Python 3只支持这种新型的类。
C.2.4 更新异常的语法
异常处理
在过去,捕获异常的语法和异常参数/实例有以下形式。
except ValueError, e:
用相同的处理程序捕获多个异常,会使用下面的语法。
except (ValueError, TypeError), e:
所需的圆括号使得一些用户迷惑,因为他们经常尝试编写看起来像下面这样的无效代码。
except ValueError, TypeError, e:
新的as关键字是为了确保你不会因为原始语法中的逗号而混淆;然而,当你试图使用相同的处理程序捕获一种以上的异常时,仍旧需要圆括号。这里有两个相同功能的新语法例子,它们展示了这种变化:
except ValueError as e:
except (ValueError, TypeError) as e:
自Python 2.6以来,之后发行的2.x版本在创建异常处理程序时都开始接受这两种形式,从而促进了移植过程。可以在PEP 3110找到关于该变化的更多信息。
抛出异常
Python 2.x中抛出异常的最受欢迎的语法如下所示。
raise ValueError, e
需要重点强调的是,你正在创建一种异常的一个实例,Python 3.x 中唯一支持的一种语法如下所示。
raise ValueError(e)
这个语法其实一点也不新鲜。在超过10年前的Python 1.5(是的,你没有看错)中就引入了这种语法,当时异常由字符串变化成类,类实例化的语法看起来更像是后者而非前者,并且我们确信你会同意这一点。
C.2.5 整数的更新
单整数类型
Python的两种不同的整数类型int和long,在Python 2.2中开始了它们的统一。那种改变现在几乎已经完成,此时新的int表现得就像一个long类型。因此,当你超过本地整数大小时不再导致OverflowError异常,并且后缀L也已经弃用。PEP 237给出了这种变化。long型仍然存在于Python 2.x版本中,但是在Python 3.0版本中它已经消失。
除法的改变
当前的除法操作符(/)不会为编程新手给出预期的答案,所以它已经发生改变以提供这种功能。如果说这种改变带来了任何争议,那就仅仅是程序员适应了向下除法(floor division)功能。为了查看混乱是如何出现的,我们可以尝试让一个编程新手认为1除以2是0(1/2 = =0),而描述这种变化的最简单方法就是举例说明。接下来就是一些摘自“Keeping up with Python:The 2.0 Release的内容,这是在2002年7月份出版的《Linux Journal》中找到的。此外,你也可以在PEP238中找到关于这个更新的更多信息。
经典除法
Python 2.x中默认的除法运算工作原理是这样的:给定两个整数操作数,“/”执行整数向下除法(截断小数部分,正如前面的例子那样)。如果两个操作数中至少有一个是浮点数,那么真除法(true division)就会发生。
>>> 1 / 2 # floor
0
>>> 1.0 / 2.0 # true
0.5
真除法
在Python 3.x中,给定任意两个数字操作数,“/”将总是返回一个浮点数。
>>> 1 / 2 # true
0.5
>>> 1.0 / 2.0 # true
0.5
如果要在Python 2.2中使用真除法,可以从future导入division或者使用-Qnew开关。
向下除法
双斜线除法运算号(//)是在Python 2.2中添加的。无论操作数是什么类型,它永远表示向下除法,并开始转换过程。
>>> 1 // 2 # floor
0
>>> 1.0 // 2.0 # floor
0.0
二进制和八进制文字
Python 2.6+中增加了小整数(minor integer)字面量的变化,以使得字面量的非十进制(十六进制、八进制和新的二进制)的格式一致。十六进制表示保持不变,仍然利用前导 0x 或0X (八进制以单个0为前导)。事实证明这种格式会使一些用户混淆,所以为了一致性已经将其更改为0o。你现在必须写成0o177,而不是0177。最后,新的二进制文字会让你提供一个整数值的各个位,以前导0b为前缀,如0b0110。此外,Python 3中不接受 0177。可以在PEP 3127找到关于整数字面量更新的更多信息。
C.2.6 迭代器无处不在
Python 3.x中内在的另一个主题就是内存保护。使用迭代器比在内存中维护整个列表更有效,特别是针对问题对象的目标动作是迭代时。当不必要时就无须浪费内存。因此,在Python 3中,早期版本语言中返回列表的代码将不再需要这么做。
例如,函数map()、filter()、range()和zip(),加上字典方法keys()、items()和values(),其中每一个都返回一些种类的迭代器。是的,如果你想查看数据,那么这个语法可以更方便,而在查看资源消耗时它更好用。这些变化大多是高级选项,如果你只使用函数的返回值来遍历,那么你将不会注意到这些改变。
