Python 3.0 的新功能

作者:

Guido van Rossum

本文介绍了 Python 3.0 相较于 2.6 的新功能。Python 3.0,也称为 “Python 3000” 或 “Py3K”,是有史以来第一个 故意向后不兼容 的 Python 版本。Python 3.0 于 2008 年 12 月 3 日发布。与典型版本相比,此次更改更多,其中许多对所有 Python 用户都很重要。然而,消化这些更改后,你会发现 Python 实际上并没有改变太多——总的来说,我们主要是修复了一些众所周知的不便和缺陷,并删除了许多旧的冗余内容。

本文不旨在提供所有新功能的完整规范,而是尝试提供一个方便的概述。有关完整详情,请参阅 Python 3.0 的文档和/或文本中引用的许多 PEP。如果你想了解某个特定功能的完整实现和设计原理,PEP 通常比常规文档包含更多详细信息;但请注意,一旦某个功能完全实现,PEP 通常不会保持更新。

由于时间限制,本文档并不像它应有的那样完整。与新版本发布时一样,源代码分发中的 Misc/NEWS 文件包含有关每个微小更改的大量详细信息。

常见障碍

本节列出了少数几个如果你习惯使用 Python 2.5 最容易遇到的更改。

视图和迭代器取代列表

一些知名的 API 不再返回列表

  • dict 方法 dict.keys()dict.items()dict.values() 返回“视图”而不是列表。例如,k = d.keys(); k.sort() 不再起作用。请改用 k = sorted(d)(此方法在 Python 2.5 中也有效,且效率相同)。

  • 此外,dict.iterkeys()dict.iteritems()dict.itervalues() 方法不再受支持。

  • map()filter() 返回迭代器。如果你确实需要一个列表且输入序列长度都相等,一个快速解决方案是将 map() 封装在 list() 中,例如 list(map(...)),但更好的解决方案通常是使用列表推导式(尤其是当原始代码使用 lambda 时),或者重写代码使其根本不需要列表。特别棘手的是因函数副作用而调用的 map();正确的转换是使用常规的 for 循环(因为创建列表只会浪费资源)。

    如果输入序列长度不相等,map() 将在最短序列终止时停止。为了与 Python 2.x 的 map() 完全兼容,还需要将序列封装在 itertools.zip_longest() 中,例如 map(func, *sequences) 变为 list(map(func, itertools.zip_longest(*sequences)))

  • range() 现在行为与过去的 xrange() 相同,只是它适用于任意大小的值。后者不再存在。

  • zip() 现在返回一个迭代器。

排序比较

Python 3.0 简化了排序比较的规则

  • 当操作数没有有意义的自然顺序时,排序比较运算符(<<=>=>)会引发 TypeError 异常。因此,像 1 < ''0 > Nonelen <= len 这样的表达式不再有效,例如 None < None 会引发 TypeError 而不是返回 False。因此,对异构列表进行排序不再有意义——所有元素都必须相互可比较。请注意,这不适用于 ==!= 运算符:不同不可比较类型的对象总是相互比较不相等。

  • sorted()list.sort() 不再接受提供比较函数的 cmp 参数。请改用 key 参数。注意,keyreverse 参数现在是“仅限关键字”的。

  • cmp() 函数应被视为已删除,__cmp__() 特殊方法不再受支持。请使用 __lt__() 进行排序,__eq__()__hash__() 配合使用,以及根据需要使用其他富比较。(如果你确实需要 cmp() 功能,可以使用表达式 (a > b) - (a < b) 作为 cmp(a, b) 的等效项。)

整数

  • PEP 237:本质上,long 重命名为 int。也就是说,只有一个名为 int 的内置整型,但它的行为大部分类似于旧的 long 类型。

  • PEP 238:像 1/2 这样的表达式返回浮点数。使用 1//2 获取截断行为。(后者的语法已经存在多年,至少自 Python 2.2 以来。)

  • sys.maxint 常量已被移除,因为整数的值不再有上限。但是,sys.maxsize 可以用作大于任何实际列表或字符串索引的整数。它符合实现的“自然”整数大小,并且通常与先前版本在同一平台上的 sys.maxint 相同(假设相同的构建选项)。

  • 长整数的 repr() 不再包含末尾的 L,因此无条件删除该字符的代码将改为截断最后一位数字。(请改用 str()。)

  • 八进制字面量不再是 0720 的形式;请改用 0o720

文本与数据取代 Unicode 与 8 位字符串

你以前所了解的关于二进制数据和 Unicode 的一切都变了。

  • Python 3.0 使用 文本 和(二进制) 数据 的概念,而不是 Unicode 字符串和 8 位字符串。所有文本都是 Unicode;然而 编码后 的 Unicode 表示为二进制数据。用于保存文本的类型是 str,用于保存数据的类型是 bytes。与 2.x 的情况最大的区别在于,Python 3.0 中任何尝试混合文本和数据的操作都会引发 TypeError,而在 Python 2.x 中,如果你混合 Unicode 和 8 位字符串,如果 8 位字符串恰好只包含 7 位(ASCII)字节,它就会工作,但如果它包含非 ASCII 值,你就会得到 UnicodeDecodeError。这种特定于值的行为多年来导致了无数的沮丧。

  • 由于这一哲学上的改变,几乎所有使用 Unicode、编码或二进制数据的代码都可能需要修改。这种改变是更好的,因为在 2.x 世界中,存在大量与混合编码和未编码文本有关的 bug。为了在 Python 2.x 中做好准备,开始对所有未编码文本使用 unicode,并且只对二进制或编码数据使用 str。然后 2to3 工具将为你完成大部分工作。

  • 你不再可以使用 u"..." 字面量表示 Unicode 文本。但是,你必须使用 b"..." 字面量表示二进制数据。

  • 由于 strbytes 类型不能混合使用,你必须始终在它们之间显式转换。使用 str.encode()str 转换为 bytes,使用 bytes.decode()bytes 转换为 str。你也可以分别使用 bytes(s, encoding=...)str(b, encoding=...)

  • str 一样,bytes 类型也是不可变的。有一个单独的 可变 类型用于保存缓冲的二进制数据,即 bytearray。几乎所有接受 bytes 的 API 也接受 bytearray。可变 API 基于 collections.MutableSequence

  • 原始字符串字面量中的所有反斜杠都按字面解释。这意味着原始字符串中的 '\U''\u' 转义序列不会被特殊处理。例如,r'\u20ac' 在 Python 3.0 中是一个包含 6 个字符的字符串,而在 2.6 中,ur'\u20ac' 是单个“欧元”字符。(当然,此更改仅影响原始字符串字面量;在 Python 3.0 中,欧元字符是 '\u20ac'。)

  • 内置的抽象类型 basestring 已被移除。请改用 strstrbytes 类型的功能没有足够的共同点来保证共享基类。2to3 工具(见下文)将所有出现的 basestring 替换为 str

  • 以文本文件模式打开的文件(open() 仍默认为此模式)始终使用编码在字符串(内存中)和字节(磁盘上)之间进行映射。二进制文件(以模式参数中的 b 打开)始终在内存中使用字节。这意味着如果文件以不正确的模式或编码打开,I/O 很可能会大声失败,而不是静默地产生不正确的数据。这也意味着即使是 Unix 用户在打开文件时也必须指定正确的模式(文本或二进制)。存在一个平台相关的默认编码,在 Unix 平台下可以通过 LANG 环境变量(有时也通过其他平台特定的区域设置相关环境变量)进行设置。在许多情况下,但并非所有情况下,系统默认编码是 UTF-8;你不应该依赖此默认值。任何读写纯 ASCII 文本之外的应用程序都应该有办法覆盖编码。不再需要使用 codecs 模块中支持编码的流。

  • sys.stdinsys.stdoutsys.stderr 的初始值现在是仅限 Unicode 的文本文件(即,它们是 io.TextIOBase 的实例)。要使用这些流读写字节数据,你需要使用它们的 io.TextIOBase.buffer 属性。

  • 文件名作为(Unicode)字符串传递给 API 并从 API 返回。这可能会带来平台特定问题,因为在某些平台上,文件名是任意字节字符串。(另一方面,在 Windows 上,文件名原生存储为 Unicode。)作为一种变通方法,大多数接受文件名的 API(例如 open()os 模块中的许多函数)都接受 bytes 对象和字符串,并且少数 API 具有请求 bytes 返回值的方法。因此,如果参数是 bytes 实例,os.listdir() 返回一个 bytes 实例列表,而 os.getcwdb() 返回当前工作目录作为 bytes 实例。请注意,当 os.listdir() 返回字符串列表时,无法正确解码的文件名将被省略,而不是引发 UnicodeError

  • 某些系统 API,例如 os.environsys.argv,当系统提供的字节无法使用默认编码解释时,也可能出现问题。设置 LANG 变量并重新运行程序可能是最好的方法。

  • PEP 3138:字符串的 repr() 不再转义非 ASCII 字符。但它仍然转义 Unicode 标准中具有不可打印状态的控制字符和码点。

  • PEP 3120:默认源代码编码现在是 UTF-8。

  • PEP 3131:标识符中现在允许使用非 ASCII 字母。(但是,标准库仍然只包含 ASCII 字符,注释中的贡献者姓名除外。)

  • StringIOcStringIO 模块已删除。相反,请导入 io 模块,并分别使用 io.StringIOio.BytesIO 处理文本和数据。

  • 另请参阅针对 Python 3.0 更新的 Unicode HOWTO

语法变更概述

本节简要概述了 Python 3.0 中所有 语法 更改。

新语法

  • PEP 3107:函数参数和返回值注解。这提供了一种标准化方式来注解函数的参数和返回值。除了可以通过 __annotations__ 属性在运行时进行自省之外,此类注解没有附加语义。其目的是通过元类、装饰器或框架鼓励实验。

  • PEP 3102:仅限关键字参数。在参数列表中 *args 之后出现的命名参数 必须 在调用时使用关键字语法指定。你也可以在参数列表中使用裸 * 来表示你不接受可变长度参数列表,但你确实有仅限关键字参数。

  • 类定义中基类列表之后允许使用关键字参数。这被用于指定元类的新约定(参见下一节),但也可以用于其他目的,只要元类支持它。

  • PEP 3104nonlocal 语句。使用 nonlocal x,你现在可以直接赋值给外部(但非全局)作用域中的变量。nonlocal 是一个新的保留字。

  • PEP 3132:扩展可迭代解包。你现在可以编写类似 a, b, *rest = some_sequence 的代码。甚至可以写 *rest, a = stuffrest 对象始终是(可能为空的)列表;右侧可以是任何可迭代对象。示例:

    (a, *rest, b) = range(5)
    

    这将 a 设置为 0b 设置为 4rest 设置为 [1, 2, 3]

  • 字典推导式:{k: v for k, v in stuff} 的含义与 dict(stuff) 相同,但更灵活。(这是 PEP 274 的证明。:-)

  • 集合字面量,例如 {1, 2}。请注意,{} 是一个空字典;使用 set() 表示一个空集合。集合推导式也受支持;例如,{x for x in stuff} 的含义与 set(stuff) 相同,但更灵活。

  • 新的八进制字面量,例如 0o720(已在 2.6 中)。旧的八进制字面量(0720)已移除。

  • 新的二进制字面量,例如 0b1010(已在 2.6 中),并且有一个新的相应内置函数 bin()

  • 字节字面量以 bB 开头,并且有一个新的相应内置函数 bytes()

改变的语法

  • PEP 3109PEP 3134:新的 raise 语句语法:raise [expr [from expr]]。见下文。

  • aswith 现在是保留字。(实际上自 2.6 以来就是。)

  • True, False, 和 None 是保留字。(2.6 已经部分强制执行了对 None 的限制。)

  • except exc, var 更改为 except exc as var。参见 PEP 3110

  • PEP 3115:新元类语法。不再使用

    class C:
        __metaclass__ = M
        ...
    

    你现在必须使用

    class C(metaclass=M):
        ...
    

    模块全局变量 __metaclass__ 不再受支持。(它是一个拐杖,用于更容易地默认使用新式类,而无需从 object 派生每个类。)

  • 列表推导式不再支持语法形式 [... for var in item1, item2, ...]。请改用 [... for var in (item1, item2, ...)]。另请注意,列表推导式具有不同的语义:它们更接近于 list() 构造函数内部生成器表达式的语法糖,特别是循环控制变量不再泄漏到周围作用域。

  • 省略号 (...) 可以作为原子表达式在任何地方使用。(以前只允许在切片中使用。)此外,它现在 必须 拼写为 ...。(以前也可以拼写为 . . .,这只是语法的一个意外。)

移除的语法

  • PEP 3113:元组参数解包已移除。你不能再写 def foo(a, (b, c)): ...。请改用 def foo(a, b_c): b, c = b_c

  • 移除了反引号(请改用 repr())。

  • 移除了 <>(请改用 !=)。

  • 移除了关键字:exec() 不再是关键字;它仍然是一个函数。(幸运的是,函数语法在 2.x 中也接受。)另请注意,exec() 不再接受流参数;你可以使用 exec(f.read()) 而不是 exec(f)

  • 整数字面量不再支持末尾的 lL

  • 字符串字面量不再支持前导的 uU

  • module import * 语法只允许在模块级别使用,不再允许在函数内部使用。

  • 相对导入唯一可接受的语法是 from .[module] import name。所有不以 . 开头的 import 形式都被解释为绝对导入。(PEP 328

  • 经典类已移除。

Python 2.6 中已存在的更改

由于许多用户可能直接从 Python 2.5 跳到 Python 3.0,本节提醒读者注意最初为 Python 3.0 设计但已反向移植到 Python 2.6 的新功能。有关更长的描述,应查阅 Python 2.6 的新功能 中的相应章节。

库更改

由于时间限制,本文档未能详尽涵盖标准库中非常广泛的更改。PEP 3108 是库主要更改的参考。以下是简要回顾:

  • 许多旧模块被移除。有些,如 gopherlib(不再使用)和 md5(被 hashlib 取代),已经由 PEP 4 弃用。其他的移除是由于移除了对各种平台(如 Irix、BeOS 和 Mac OS 9)的支持(参见 PEP 11)。一些模块也因缺乏使用或存在更好的替代品而被选定在 Python 3.0 中移除。有关详尽列表,请参见 PEP 3108

  • bsddb3 包已被移除,因为事实证明它在核心标准库中的存在给核心开发人员带来了特别大的负担,原因在于测试不稳定和 Berkeley DB 的发布计划。然而,该包仍然活跃,并在外部维护于 https://www.jcea.es/programacion/pybsddb.htm

  • 有些模块因其旧名称不符合 PEP 8 或其他各种原因而被重命名。以下是列表:

    旧名称

    新名称

    _winreg

    winreg

    ConfigParser

    configparser

    copy_reg

    copyreg

    队列

    queue

    SocketServer

    socketserver

    markupbase

    _markupbase

    repr

    reprlib

    test.test_support

    test.support

  • Python 2.x 中常见的模式是有一个纯 Python 实现的模块版本,以及一个可选的 C 扩展实现的加速版本;例如,picklecPickle。这使得每个使用这些模块的用户都必须承担导入加速版本并回退到纯 Python 版本的负担。在 Python 3.0 中,加速版本被视为纯 Python 版本的实现细节。用户应该始终导入标准版本,它会尝试导入加速版本并回退到纯 Python 版本。pickle / cPickle 对受到了这种处理。profile 模块在 3.1 的列表中。StringIO 模块已在 io 模块中变为一个类。

  • 一些相关模块已被分组到包中,并且通常子模块名称已简化。由此产生的新包是:

    • dbmanydbmdbhashdbmdumbdbmgdbmwhichdb)。

    • htmlHTMLParserhtmlentitydefs)。

    • httphttplibBaseHTTPServerCGIHTTPServerSimpleHTTPServerCookiecookielib)。

    • tkinter(除了 turtle 之外的所有 Tkinter 相关模块)。turtle 的目标受众并不关心 tkinter。另请注意,自 Python 2.6 起,turtle 的功能已大大增强。

    • urlliburlliburllib2urlparserobotparse)。

    • xmlrpcxmlrpclibDocXMLRPCServerSimpleXMLRPCServer)。

标准库模块的其他一些更改,PEP 3108 未涵盖:

  • 移除了 sets。请使用内置的 set() 类。

  • sys 模块的清理:移除了 sys.exitfunc()sys.exc_clear()sys.exc_typesys.exc_valuesys.exc_traceback。(注意 sys.last_type 等仍然保留。)

  • array.array 类型的清理:read()write() 方法已移除;请改用 fromfile()tofile()。此外,数组的 'c' 类型码已移除——请使用 'b' 表示字节或 'u' 表示 Unicode 字符。

  • operator 模块的清理:移除了 sequenceIncludes()isCallable()

  • thread 模块的清理:acquire_lock()release_lock() 已移除;请改用 acquire()release()

  • random 模块的清理:移除了 jumpahead() API。

  • new 模块已移除。

  • os.tmpnam()os.tempnam()os.tmpfile() 函数已移除,取而代之的是 tempfile 模块。

  • tokenize 模块已更改为使用字节。主要入口点现在是 tokenize.tokenize(),而不是 generate_tokens。

  • string.letters 及其朋友(string.lowercasestring.uppercase)已移除。请改用 string.ascii_letters 等。(移除的原因是 string.letters 及其朋友具有区域设置特定的行为,这对于如此吸引人的全局“常量”来说是个坏主意。)

  • 模块 __builtin__ 已重命名为 builtins(去掉了下划线,添加了 's')。大多数全局命名空间中的 __builtins__ 变量未更改。要修改内置函数,你应该使用 builtins,而不是 __builtins__

PEP 3101:字符串格式化的新方法

  • 一个新的内置字符串格式化操作系统取代了 % 字符串格式化运算符。(然而,% 运算符仍受支持;它将在 Python 3.1 中弃用,并在稍后从语言中移除。)请阅读 PEP 3101 了解所有详细信息。

异常更改

引发和捕获异常的 API 已清理,并添加了强大的新功能。

  • PEP 352:所有异常都必须(直接或间接)派生自 BaseException。这是异常层次结构的根。作为建议,这不是新内容,但继承自 BaseException要求 是新内容。(Python 2.6 仍然允许引发经典类,并且对你可以捕获的内容没有限制。)因此,字符串异常终于真正彻底地消失了。

  • 几乎所有异常实际上都应该派生自 ExceptionBaseException 应该只用作仅在顶层处理的异常的基类,例如 SystemExitKeyboardInterrupt。处理除后者以外所有异常的推荐惯例是使用 except Exception

  • StandardError 已移除。

  • 异常不再作为序列行为。请改用 args 属性。

  • PEP 3109:引发异常。你现在必须使用 raise Exception(args) 而不是 raise Exception, args。此外,你不能再显式指定回溯;相反,如果你 必须 这样做,你可以直接赋值给 __traceback__ 属性(见下文)。

  • PEP 3110:捕获异常。你现在必须使用 except SomeException as variable 而不是 except SomeException, variable。此外,当 except 块结束时,variable 会被显式删除。

  • PEP 3134:异常链。有两种情况:隐式链和显式链。隐式链发生在 exceptfinally 处理程序块中引发异常时。这通常是由于处理程序块中的错误造成的;我们称之为 次要 异常。在这种情况下,原始异常(正在处理的异常)将作为次要异常的 __context__ 属性保存。显式链使用此语法调用:

    raise SecondaryException() from primary_exception
    

    (其中 primary_exception 是任何产生异常对象的表达式,可能是之前捕获的异常)。在这种情况下,主异常存储在次要异常的 __cause__ 属性上。当发生未处理的异常时打印的回溯会遍历 __cause____context__ 属性链,并为链的每个组件打印单独的回溯,主异常位于顶部。(Java 用户可能会识别此行为。)

  • PEP 3134:异常对象现在将其回溯存储为 __traceback__ 属性。这意味着异常对象现在包含与异常相关的所有信息,使用 sys.exc_info() 的原因更少(尽管后者并未移除)。

  • 当 Windows 无法加载扩展模块时,一些异常消息得到了改进。例如,error code 193 现在是 %1 is not a valid Win32 application。字符串现在处理非英语语言环境。

其他杂项更改

运算符和特殊方法

  • != 现在返回 == 的相反值,除非 == 返回 NotImplemented

  • 语言中已删除“未绑定方法”的概念。现在,当将方法作为类属性引用时,你会得到一个纯函数对象。

  • __getslice__()__setslice__()__delslice__() 已被移除。语法 a[i:j] 现在转换为 a.__getitem__(slice(i, j))(或 __setitem__()__delitem__(),分别用于赋值或删除目标时)。

  • PEP 3114:标准的 next() 方法已更名为 __next__()

  • 特殊方法 __oct__()__hex__() 已移除 – oct()hex() 现在使用 __index__() 将参数转换为整数。

  • 移除了对 __members____methods__ 的支持。

  • 命名为 func_X 的函数属性已重命名为使用 __X__ 形式,从而将这些名称从函数属性命名空间中释放出来供用户定义属性使用。也就是说,func_closurefunc_codefunc_defaultsfunc_dictfunc_docfunc_globalsfunc_name 分别重命名为 __closure____code____defaults____dict____doc____globals____name__

  • __nonzero__() 现在是 __bool__()

内置函数

  • PEP 3135:新的 super()。现在,您可以在不带参数的情况下调用 super(),并且(假设它是在 class 语句中定义的常规实例方法中)将自动选择正确的类和实例。带参数时,super() 的行为保持不变。

  • PEP 3111raw_input() 已重命名为 input()。也就是说,新的 input() 函数从 sys.stdin 读取一行并返回,并去除末尾的换行符。如果输入过早终止,它会引发 EOFError。要获得 input() 的旧行为,请使用 eval(input())

  • 添加了一个新的内置函数 next(),用于调用对象上的 __next__() 方法。

  • round() 函数的舍入策略和返回类型已更改。精确的中间情况现在舍入到最接近的偶数结果,而不是远离零。(例如,round(2.5) 现在返回 2 而不是 3。)round(x[, n]) 现在委托给 x.__round__([n]),而不是总是返回浮点数。当用单个参数调用时,它通常返回一个整数;当用两个参数调用时,它返回与 x 类型相同的值。

  • intern() 移至 sys.intern()

  • 已移除:apply()。现在,使用 f(*args) 而不是 apply(f, args)

  • 已移除 callable()。现在,使用 isinstance(f, collections.Callable) 而不是 callable(f)。函数 operator.isCallable() 也已移除。

  • 已移除 coerce()。由于经典类已不复存在,此函数不再有用。

  • 已移除 execfile()。现在,使用 exec(open(fn).read()) 而不是 execfile(fn)

  • 已移除 file 类型。请使用 open()。现在,io 模块中 open 可以返回几种不同类型的流。

  • 已移除 reduce()。如果您确实需要它,请使用 functools.reduce();然而,99% 的情况下,显式的 for 循环更具可读性。

  • 已移除 reload()。请使用 imp.reload()

  • 已移除。dict.has_key() – 请改用 in 运算符。

构建和 C API 更改

由于时间限制,这里列出了 C API 更改的 *非常* 不完整的列表。

  • 已停止支持多个平台,包括但不限于 Mac OS 9、BeOS、RISCOS、Irix 和 Tru64。

  • PEP 3118:新缓冲区 API。

  • PEP 3121:扩展模块初始化与终结。

  • PEP 3123:使 PyObject_HEAD 符合标准 C。

  • 不再支持 C API 的受限执行。

  • PyNumber_Coerce()PyNumber_CoerceEx()PyMember_Get()PyMember_Set() C API 已移除。

  • 新的 C API PyImport_ImportModuleNoBlock(),其功能类似于 PyImport_ImportModule(),但不会在导入锁上阻塞(而是返回错误)。

  • 布尔转换 C 级槽和方法已重命名:nb_nonzero 现在是 nb_bool

  • 从 C API 中移除了 METH_OLDARGSWITH_CYCLE_GC

性能

Python 3.0 泛化的最终结果是,Python 3.0 运行 pystone 基准测试比 Python 2.5 慢约 10%。最大的原因很可能是取消了对小整数的特殊处理。仍有改进的空间,但这将在 3.0 发布后进行!

移植到 Python 3.0

要将现有 Python 2.5 或 2.6 源代码移植到 Python 3.0,最佳策略如下:

  1. (先决条件:) 从出色的测试覆盖率开始。

  2. 移植到 Python 2.6。这应该不比从 Python 2.x 移植到 Python 2.(x+1) 的平均工作量大。确保所有测试都通过。

  3. (仍使用 2.6:) 启用 -3 命令行开关。这将启用有关 3.0 中将移除(或更改)的功能的警告。再次运行您的测试套件,并修复您收到警告的代码,直到没有警告,并且所有测试仍然通过。

  4. 在您的源代码树上运行 2to3 源代码到源代码转换器。在 Python 3.0 下运行转换结果。手动修复任何剩余问题,直到所有测试再次通过。

不建议尝试编写在 Python 2.6 和 3.0 下均不变运行的源代码;您将不得不使用非常扭曲的编码风格,例如避免 print 语句、元类等等。如果您正在维护一个需要同时支持 Python 2.6 和 Python 3.0 的库,最好的方法是通过编辑 2.6 版本的源代码并再次运行 2to3 转换器来修改上述第 3 步,而不是编辑 3.0 版本的源代码。

有关将 C 扩展移植到 Python 3.0 的信息,请参阅 将扩展模块移植到 Python 3