Python 3000 进度报告(有点长!)

摘要

这是大家期待已久的对Python3000项目最新进展的报告。在过去两个月的进度中,产生了许多激动人心的新特性。 在接下来的两个月中,我会分几次亲自来向大家展示这些东西。

项目简介

早期历史

最开始冒出 Python 3000 这个想法貌似还是在 2000 年 Python 大会上。 取 Python 3000 这个名字是从 Windows 2000 这个名字中得来的灵感。 很长一段时间以来,Python 已经积累了许多让我后悔的设计和瑕疵,如果不破坏向后兼容性,根本没办法修正它们。 于是我想为了 Python 日后能够继续轻装上阵,让 Python 3000 成为第一个不考虑向后兼容性的版本。

近期历史

大概一年半以前(并非巧合,因为那个时候我已经在Google工作了,这让我可以把比以前多得多的时间花在 Python 上面。) 我觉得是时候要开始实际地设计和规划 Python 3000 了。 我与 Python 开发者及用户社区一起整了一个计划出来。 我们建了一个新的 PEP (Python Enhancement Proposals) 系列, 他们的序号从 3000 开始。 那个时候我们已经有了一个 PEP 3000 了,由社区里面其他一些人维护的,那上面已经记录了 许多适合在 Python 3000 中实现的想法清单。 这个 PEP 现在已经更名为 PEP 3100 。 而 PEP 3000 现在用来描述整个项目的设计 哲学和时间表了。

从那时候起我们不敢说是撼动了一座大山, 但是在邮件列表 python-dev 及其后的 python-3000 的桥洞下面却着实是有着一股巨流在涌动。

暂定时间表

大约一年前公布了第一份时间表,那时候我们希望能在2007上半年的末尾发布第一个3.0 alpha版本, 然后在一年后发布 3.0 正式版。(Python 3.0 才是发布的版本号,"Python 3000" 和 "Py3K" 只是项目的 code name )

这个时间表已经向后推迟了一些;我们现在希望能在8月份结束的时候发布第一个 alpha 版本。最终版的发布时间自然也要向后顺延了。(这次时间表的推迟主要是因为过渡到全 Unicode 字符串和 可变的字节数组需要做大量的工作。另外也是因为我“放权”不够,没有把更多的工作交给其他 开发人员来做;这个错误我一直在疯狂地弥补。)

Python 2.6

我们计划在 3.0 发布前的几个月发布一个 Python 2.6 的姐妹版,发布这个版本的 4 个月前会先发布一个 alpha 版本(也就是在第一个 3.0 alpha 版发布之后)。 下面两节会解释这个版本所肩负的任务。 如果你没兴趣跟踪最新的变化(living on the bleeding edge), 那你完全可以使用 Python 2.6 ,它和 2.5 区别并不大。

兼容性和过渡

兼容性

Python 3.0 会完全地破坏向后兼容性。 我们甚至不期望某个兼容的子集(当然肯定会有这么一个子集, 只不过我们不打算支持在这个子集中编写重要程序。它 只不过是从 2.6 到 3.0 恰巧没变的那些特性的集合罢了)。

Python 2.6 则会完全保持与 Python 2.5 的向后兼容性 (并尽可能保持与更早版本的向后兼容性),而且它还会 以下面几种方式支持某种意义上的向前兼容性:

  • Python 2.6 会支持一个“Py3k 警告模式”,它会在运行时对那些在 Python 3.0 下无法工作的特性发出警告,比如说把 range() 函数返回 的东西当列表用。
  • Python 2.6 会包含 Py3k 中许多特性的“向后移植版”,可能是通过 __future__ 语句 激活这些特性,或者是允许新旧语法同时存在(只要这个新语法在 2.4 中不是个语法错误)。
  • 作为 2.6 版向前兼容的补充,到时候还会有一个独立的 代码转换工具 。这个工具可以做上下文无关的代码转换。举个(非常简单的)例子,它能把 apply(f, args) 转换成 f(*args) 。 不过这个工具并不能做数据流分析和类型推导,所以它会简单地直接把 apply 当做 旧版本中的一个内置函数来处理。

过渡期开发

对于需要同时支持 Python2.6 和 Python3.0 的项目,我们推荐以下开发模式:

  1. 从卓越的单元测试开始,最好是能几乎覆盖整个项目。
  2. 然后将项目移植到 Python 2.6。
  3. 开启 Py3k 警告模式。
  4. 测试、修改循环直到消除所有警告。
  5. 使用 2to3(译注:上面提到过的代码转换工具)工具将代码转换到 3.0 的语法。 不要手动编辑输出的代码!
  6. 在 3.0 下测试转换后的代码。
  7. 如果发现问题,在针对 Python 2.6 的代码中做修改,然后跳到第 4 步继续。
  8. 发布的时候,分别发布 2.6 和 3.0 的 tarball (或任何其他你们使用的打包格式)。

转换工具能够产生高质量的代码,而且在许多场合同手动转换的代码没有什么区别。 不过,我们还是 强烈 建议不要编辑 3.0 的代码,除非你打算以后对 2.6 版本的代码只会进行维护而不加入新功能(也就是通常你把 2.6 的代码移到一个维护分支的时候)。

完成第二步需要花费的功夫和通常迁移到一个新的 Python 版本所需要的差不多。我们正在努力 让 2.5 到 2.6 的过渡尽可能地流畅。

如果代码转换工具和 Python 2.6 的向前兼容特性能够符合预期目标的话,从第三步到第七步应该不会比 以前一个小版本号的迁移(从Python 2.x 到 2.(x+1))更麻烦。

新特性介绍

如果把新特性全部列在这里,那就太多了;所以我建议你去看相关的 PEP。 不过,我还是要强调一些我发现很重要也是很多人感兴趣和比较有争议的一些特性。

Unicode,编解码器和 I/O

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

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

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

新的 I/O 库

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

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

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

print 和 格式化

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

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

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

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

对 class 和类型系统的改变

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

  • 类装饰器。他们和函数装饰器非常像::

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

    def foobar(a: Integer, b: Sequence) -> String:
    ...
  • 新的 metaclass 语法。以前是在 class 定义体中设置一个变量 __metaclass__ , 现在你得在 class 定义头中指定一个关键字参数,比如::

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

  • 你可以动态地指定基类,比如::

    bases = (B1, B2)
    class C(*bases):
    ...
  • 在 class 定义头中还可以使用其他的关键字参数;这些参数都被传给 metaclass 的 __new__ 方法。

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

  • 抽象基类(Abstract Base Classes, ABC)。如果你想要定义一个 class ,它的实例 的行为类似 mapping (举个例子),你就可以继承 class abc.Mapping 。 一方面,这个 class 提供 mix-in 的行为,可以提供大部分 UserDictDictMixin 的功能。另一方面,系统地使用这种 ABC 能够帮助大型框架减少猜测 并获得正确的行为:在 Python 2 中,要判断一个实现了 __getitem__() 方法的对象究竟是序列(sequence)还是映射(mapping),还真不是那么简单的事情。 我们提供了下面这些ABC:Hashable, 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

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

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

其他重要变化

一些精选!

异常

  • 字符串异常去掉了。
  • 所有异常必须继承自 BaseException ,最好是直接继承 Exception
  • 去掉 StandardException
  • 异常不再拥有序列(sequence)的行为。不过现在他们有了一个属性 args , 它就是由传给异常构造函数的参数所组成的序列。
  • 语法: except E, e: 变成 except E as e: ,这使得语句 except E1, E2 不再导致混淆。
  • except 子句中 as 后面的名字在退出 except 子句时会被强制移除。
  • sys.exc_info() 变得多余了(也许会被去掉):取而代之的是 e.__class__ 是异常 类型, e.__traceback__ 是 traceback 。
  • 如果在 exceptfinally 子句中发生异常,会添加另一个可选的属性 __context__ ,其值为前一个异常对象。重抛出异常时,使用 raise E1 from E2 可以明确地设置属性 __cause__ 的值。
  • 以前的 raise 语法变种 raise E, eraise E, e, tb 已经去掉了。

整数

  • 只会有一个内置的整数类型,它就是 int ,它的行为就和 Python 2 中的 long 一样。后缀 'L' 消失。
  • 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 中 对应的副本目前的行为一样)。

其他

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

标准库的革命

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

最后

我前面提到过 lambda 将继续存活吗?我仍然不断收到希望保留 lambda 的请求, 所以我想我得再强调一次。别担心,这个请求我在一年多前就已经同意了。

演讲计划

在最近的几个会议上,我会亲自讲解这些(还有更多的)东西:

  • USENIX, June 20, 11am, Santa Clara, CA (a Q&A format) http://www.usenix.org/events/usenix07/
  • Google's Phoenix, AZ office, June 21, 7pm (open to the public!)
  • O'Reilly foo camp, June 23, Sebastopol, CA (get me drunk first :-) http://foocamp.crowdvine.com
  • CWI, Amsterdam, July 5, 3pm (Python's cradle!) http://www.cwi.nl
  • EuroPython, Vilnius, Lithuania, July 10 (keynote) http://www.europython.org/sections/tracks_and_talks/draft-timetable
  • OSCON, Portland, OR, July 26, 2:35pm (Python track) http://conferences.oreillynet.com/cs/os2007/view/e_sess/12753

原文: http://www.artima.com/weblogs/viewpost.jsp?thread=208549
译者: 黄毅( http://codeplayer.blogspot.com )

你可能感兴趣的:(Python编程思想)