`

Python 3000 新特性介绍

阅读更多

Python 3000 新特性介绍<o:p></o:p>

Unicode,编解码器和 I/O<o:p></o:p>

Python 3000正在迁移到 Java 处理 Unicode 的模型,那就是:(不可变的)文本字符串就是 Unicode ,而二进制数据则由另一个(可变的)“字节数组”的类型来表达。 另外词法分析器也对 Unicode 更加友好了:默认的源代码的编码将会是 UTF-8 而且非 ASCII 字母也可以用在标识符里面了,现在还在进行一些这方面讨论,主要集中在标准化、明确非 ASCII 字母表、是否应该某种程度地支持自右向左的程序几个问题上。不过标准库自然还是会一如既往地只使用 ASCII 字符做标识符,并且限制在注释和字面字符串(string literals)中使用非 ASCII 字母,在单元测试中测试一些 Unicode 特性或者是作者名字的情形除外。<o:p></o:p>

我们将会使用 "..." '...' 来表达字面 Unicode 字符串,而 b"..." b'...' 用来表达字面字节数组。比如, b'abc' 等价于使用表达式 bytes([97, 98, 99]) 创建的字节数组。<o:p></o:p>

我们采用一种和以前稍微不同的编解码器:在 Python 2 中,编解码器既可以接受 Unicode 也可以接受8位字节数组作为输入,同样也可以输出这两种对象的任何一种,而在 Py3k 中编码过程总是从 Unicode 字符串到字节数组的转换,而解码过程则总是相反的方向。 这意味着我们必须丢弃一些不符合这个模型的编解码器,比如 rot13base64 bz2 (当然我们仍然支持这些转换操作,只不过不通过 encode/decode API 了)。<o:p></o:p>

新的 I/O <o:p></o:p>

针对上面的这些变化 I/O 库自然要做相应的变化了。正好我也早有重写这个库的意思, 以前我就想要除掉它对 C stdio 库的依赖。而现在字节数组和字符串的区别也需要 它的 API 有一个(细微的)变化,the two projects were undertake hand in hand 在这个新库中,打开二进制流(使用模式 "rb" "wb" 打开)和文本流 (模式名中不包含 "b" )之间将会有明显的区别。文本流有一个新属性,encoding,你可以在打开流的时候显式地当参数传给它;如果没有指定编码,就会使用系统默认的编码( 打开一个存在的文件时也可以猜它的编码)。<o:p></o:p>

对二进制流的读操作返回的是字节数组,而对文本流的读操作返回(Unicode)字符串; 写操作也类似。给二进制流写入字符串或者给文本流写入字节数组都会抛出异常。<o:p></o:p>

除此以外,API 基本还是和以前差不多。同样会有一个内置的 open() 函数, 新的 I/O 库在 io 模块中。这个模块还包含不同类型流的抽象基类 (抽象基类见下文),一个 StringIO 的新实现,还有一个类似 StringIO BytesIO,它实现了一个二进制流,因此也就只能读写字节数组。<o:p></o:p>

print和格式化<o:p></o:p>

还有两个和 I/O 有关的新特性:古老的 print 语句变成了 print() 函数, 而诡异的字符串格式化操作符 % 也被字符串对象的新方法 format() 取代。<o:p></o:p>

print 变成函数常常让人大吃一惊(makes some eyes roll)。但是它确实还是有一些 好处的,比如:这样可以方便地将使用 print 的代码重构成(比如说)使用 logging 模块; 并且 print 的语法一直以来也都存在一些争议,比如 >>file 和末尾跟一个逗号的独特 语义。现在由关键字参数来完成这些任务,大家都满意了。<o:p></o:p>

同样,新的 format() 方法也避免了一些 % 操作符的缺陷,特别是语句 "%s" % x x 是个元组时让人意外的结果,和许多人经常犯的忘记敲 %(name)s 最后的这个 's' 的失误。新的格式化字符串使用 {0}{1}{2} ... 来引用传给 format() 方法位置参数, {a}, {b}, ... 来引用关键字参数。还有其他一些特性如用 {a.b.c} 来访问对象属性,甚至可以用 {a[b]} 访问字典和序列。可以用 {a:8} 来指定转换字符串的长度,使用这个语法也可以传递其他格式化选项。<o:p></o:p>

format() 方法还可以不同的维度进行扩展:通过定义 __format__() 方法,任何类型可以定义 他们自己格式化的方式和解释格式化参数的方式;你还可以创建自定义的格式化 class ,它可以用来 (比如)自动将局部名字空间当作格式化的参数。<o:p></o:p>

class和类型系统的改变<o:p></o:p>

你可能已经猜到了,"classic classes" 终于退出了历史舞台。内置类型 object 成为新 class 的默认基类。这使得一系列新特性得以实现:<o:p></o:p>

<!---->1.         <!---->类装饰器。他们和函数装饰器非常像::<o:p></o:p>

<!---->Ø  <!---->@art_decoclass C:    ...<o:p></o:p>

函数和方法的定义可以加上“签名(annotated)”,语言核心本身并不给这些签名赋予 特殊的含义(只是在内省时会提供这些元数据),不过一些标准库却可以;比如, generic function (见下文)可以使用这些元数据。这个新语法可读性非常好::<o:p></o:p>

<!---->Ø  <!---->def foobar(a: Integer, b: Sequence) -> String:    ...<o:p></o:p>

<!---->2.         <!---->新的 metaclass 语法。以前是在 class 定义体中设置一个变量 __metaclass__ 现在你得在 class 定义头中指定一个关键字参数,比如::<o:p></o:p>

<!---->Ø  <!---->class C(bases, metaclass=MyMeta):    ...<o:p></o:p>

自定义 class 字典。如果 metaclass 定义了 __prepare__() 方法,那么在进入 class 定义体之前会调用它,用它返回的对象取代标准的字典,而 class 定义体中的属性就用这个对象进行存储。比如说,你可以用这个特性来实现一个 "struct" 类型,因为对于 "struct" 来说属性定义的 顺序是非常关键的。<o:p></o:p>

<!---->3.         <!---->你可以动态地指定基类,比如::<o:p></o:p>

<!---->Ø  <!---->bases = (B1, B2)class C(*bases):    ...<o:p></o:p>

class 定义头中还可以使用其他的关键字参数;这些参数都被传给 metaclass __new__ 方法。<o:p></o:p>

你可以通过定义 __instancecheck__() __subclasscheck__() 方法 重载相应的 __isinstance__() __issubclass__() 语义。 如果定义了这两个方法, isinstance(x, C) 等价于 C.__instancecheck__(x) ,而 issubclass(D, C) 等价于 C.__subclasscheck__(D) <o:p></o:p>

抽象基类(Abstract Base Classes, ABC)。如果你想要定义一个 class ,它的实例 的行为类似 mapping (举个例子),你就可以继承 class abc.Mapping 一方面,这个 class 提供 mix-in 的行为,可以提供大部分 UserDict DictMixin 的功能。另一方面,系统地使用这种 ABC 能够帮助大型框架减少猜测 并获得正确的行为:在 Python 2 中,要判断一个实现了 __getitem__() 方法的对象究竟是序列(sequence)还是映射(mapping),还真不是那么简单的事情。 我们提供了下面这些ABCHashable, Iterable, Iterator, Sized, Container, Callable; Set, MutableSet; Mapping, MutableMapping; Sequence, MutableSequence; Number, Complex, Real, Rational, Integer io 模块 也定义了许多ABC,所以在 python 历史上,终于对以前定义模糊的“file-like”这个概念 第一次有了明确的规范。ABC 框架的强大能力来源于(从 zope interface 中学来的)能够 注册某一个 class X ,让它“虚拟地继承于”一个 ABC Y,而 X Y 可以由不同的作者编写,也可以出现在不同的包中。(需要澄清的是:如果使用的是虚拟继承,class Y mix-in 行为并不会 作用在 class X 上;唯一的效果只是 issubclass(X, Y) 返回 True <o:p></o:p>

抽象基类(ABC)需要确保子类实现了完整的接口,为了支持抽象基类的定义, 我们引入装饰器 @abc.abstractmethod 来定义抽象方法(只能用在 metaclass abc.ABCMeta 或其子类的 class 中)。<o:p></o:p>

Generic Functions。是否包含这一特性(PEP 3124 中有描述),其实还不是很确定 ,因为这个 PEP 的进展越来越慢现在几乎停滞了。希望它的步伐能够重新恢复过来。它支持基于所有参数类型的函数分派(译注:简单地说就是静态语言中的重载的动态语言版本), 而不仅仅是通常的只基于目标对象( self )的分派。<o:p></o:p>

其他重要变化<o:p></o:p>

<!---->1)        <!---->异常<o:p></o:p>

字符串异常去掉了。所有异常必须继承自 BaseException ,最好是直接继承 Exception 。去掉 StandardException 。异常不再拥有序列(sequence)的行为。不过现在他们有了一个属性 args 它就是由传给异常构造函数的参数所组成的序列。<o:p></o:p>

<!---->2)        <!---->语法<o:p></o:p>

except E, e: 变成 except E as e: ,这使得语句 except E1, E2 不再导致混淆。except 子句中 as 后面的名字在退出 except 子句时会被强制移除。sys.exc_info() 变得多余了(也许会被去掉):取而代之的是 e.__class__ 是异常 类型, e.__traceback__ traceback 。如果在 except finally 子句中发生异常,会添加另一个可选的属性 __context__ ,其值为前一个异常对象。重抛出异常时,使用 raise E1 from E2 可以明确地设置属性 __cause__ 的值。以前的 raise 语法变种 raise E, e raise E, e, tb 已经去掉了。<o:p></o:p>

<!---->3)        <!---->整数<o:p></o:p>

只会有一个内置的整数类型,它就是 int ,它的行为就和 Python 2 中的 long 一样。后缀 'L' 消失。<o:p></o:p>

1/2 会返回 0.5 ,而不像以前那样返回 0 (使用 1//2 来返回 0)。表达 8 进制字面量的语法改成了 0o777 ,以免年轻程序员搞糊涂。二进制字面量:0b101 == 5, bin(5) == '0b101'。使用迭代器(Iterator)或可迭代对象(Iterable)而非列表(list)dict.keys() dict.items() 返回集合(其实是视图); dict.values() 返回一个可迭代的容器视图。 iter*() 系列消失。range() 返回以前的 xrange() 返回的对象;而 xrange() 消失。zip(), map(), filter() 返回可迭代对象(和他们在 itertools 对应的副本目前的行为一样)。<o:p></o:p>

<!---->4)        <!---->其他<o:p></o:p>

reduce() 没了。这并不意味着我不喜欢高端函数(higher-order functions); 只不过是好像所有使用 reduce() 的代码使用原始的 for 循环重写后都便得 更可读了。不过 lambda 继续存在。倒单引号(`) 语法,通常可读性都不好,去掉了(直接使用 repr() 吧),同样 <> 操作符也 去掉了。(使用 != ,这是对 TOOWIDI 原则一个臭名昭著的违反!)<o:p></o:p>

<!---->5)        <!---->标准库的革命<o:p></o:p>

关于对标准库的变化我不想说太多,因为这部分要到 3.0a1 发布之后才会开始实际的工作。而且我本人也不会去管它(语言核心就够我弄的了)。显然的是我们会除掉许多不支持了的或者 过时了的垃圾模块(比如许多模块只适用于 SGI IRIX),另外我们还在努力重命名那些以 CapWords 风格命名的模块名字,像 StringIO 或者 UserDict<o:p></o:p>

 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics