2to3 --- 自动化的 Python 2 到 3 代码转写¶
2to3 是一个 Python 程序,它可以读取 Python 2.x 的源代码并使用一系列的 修复器 来将其转换为合法的 Python 3.x 代码。 标准库已包含了丰富的修复器,这足以处理几乎所有代码。 不过 2to3 的支持库 lib2to3 是一个很灵活通用的库,所以还可以编写你自己的 2to3 修复器。
Deprecated since version 3.11, will be removed in version 3.13: lib2to3 模块在 Python 3.9 中被标记为即将弃用 (在导入时引发 PendingDeprecationWarning) 并在 Python 3.11 中正式弃用 (引发 DeprecationWarning)。 2to3 工具是它的一部分。 它将在 Python 3.13 中被移除。
使用 2to3¶
2to3 通常会作为脚本和 Python 解释器一起安装,你可以在 Python 根目录的 Tools/scripts 文件夹下找到它。
2to3 的基本调用参数是一个需要转换的文件或目录列表。对于目录,会递归地寻找其中的 Python 源码。
这里有一个 Python 2.x 的源码文件,example.py:
def greet(name):
    print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)
它可以在命令行中使用 2to3 转换成 Python 3.x 版本的代码:
$ 2to3 example.py
这个命令会打印出和源文件的区别。通过传入 -w 参数,2to3 也可以把需要的修改写回到原文件中(除非传入了 -n 参数,否则会为原始文件创建一个副本):
$ 2to3 -w example.py
在转换完成后,example.py 看起来像是这样:
def greet(name):
    print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name)
注释和缩进都会在转换过程中保持不变。
默认情况下,2to3 会执行 预定义修复器 的集合。使用 -l 参数可以列出所有可用的修复器。使用 -f 参数可以明确指定需要使用的修复器集合。而使用 -x 参数则可以明确指定不使用的修复器。下面的例子会只使用 imports 和 has_key 修复器运行:
$ 2to3 -f imports -f has_key example.py
这个命令会执行除了 apply 之外的所有修复器:
$ 2to3 -x apply example.py
有一些修复器是需要 显式指定 的,它们默认不会执行,必须在命令行中列出才会执行。比如下面的例子,除了默认的修复器以外,还会执行 idioms 修复器:
$ 2to3 -f all -f idioms example.py
注意这里使用 all 来启用所有默认的修复器。
有些情况下 2to3 会找到源码中有一些需要修改,但是无法自动处理的代码。在这种情况下,2to3 会在差异处下面打印一个警告信息。你应该定位到相应的代码并对其进行修改,以使其兼容 Python 3.x。
2to3 也可以重构 doctests。使用 -d 开启这个模式。需要注意*只有* doctests 会被重构。这种模式下不需要文件是合法的 Python 代码。举例来说,reST 文档中类似 doctests 的示例也可以使用这个选项进行重构。
-v 选项可以输出更多转换程序的详细信息。
由于某些 print 语句可被解读为函数调用或是语句,2to3 并不总能读取包含 print 函数的文件。 当 2to3 检测到存在 from __future__ import print_function 编译器指令时,会修改其内部语法将 print() 解读为函数。 这一变动也可以使用 -p 旗标手动开启。 使用 -p 来为已转换过 print 语句的代码运行修复器。 也可以使用 -e 将 exec() 解读为函数。
-o 或 --output-dir 选项可以指定将转换后的文件写入其他目录中。由于这种情况下不会覆写原始文件,所以创建副本文件毫无意义,因此也需要使用 -n 选项来禁用创建副本。
Added in version 3.2.3: 增加了 -o 选项。
-W 或 --write-unchanged-files 选项用来告诉 2to3 始终需要输出文件,即使没有任何改动。这在使用 -o 参数时十分有用,这样就可以将整个 Python 源码包完整地转换到另一个目录。这个选项隐含了 -w 选项,否则等于没有作用。
Added in version 3.2.3: 增加了 -W 选项。
--add-suffix 选项接受一个字符串,用来作为后缀附加在输出文件名后面的后面。由于写入的文件名与原始文件不同,所以没有必要创建副本,因此 -n 选项也是必要的。举个例子:
$ 2to3 -n -W --add-suffix=3 example.py
这样会把转换后的文件写入 example.py3 文件。
Added in version 3.2.3: 增加了 --add-suffix 选项。
将整个项目从一个目录转换到另一个目录可以用这样的命令:
$ 2to3 --output-dir=python3-version/mycode -W -n python2-version/mycode
修复器¶
转换代码的每一个步骤都封装在修复器中。可以使用 2to3 -l 来列出可用的修复器。之前已经提到,每个修复器都可以独立地打开或是关闭。下面会对各个修复器做更详细的描述。
- apply¶
- 移除对 - apply()的使用,举例来说,- apply(function, *args, **kwargs)会被转换成- function(*args, **kwargs)。
- asserts¶
- 将已弃用的 - unittest方法替换为正确的。- 从 - 到 - failUnlessEqual(a, b)- assertEquals(a, b)- failIfEqual(a, b)- assertNotEquals(a, b)- failUnless(a)- assert_(a)- failIf(a)- failUnlessRaises(exc, cal)- failUnlessAlmostEqual(a, b)- assertAlmostEquals(a, b)- failIfAlmostEqual(a, b)- assertNotAlmostEquals(a, b)
- buffer¶
- 将 - buffer转换为- memoryview。这个修复器是可选的,因为- memoryviewAPI 和- buffer很相似,但不完全一样。
- dict¶
- 修复字典迭代方法。 - dict.iteritems()会转换成- dict.items(),- dict.iterkeys()会转换成- dict.keys(),- dict.itervalues()会转换成- dict.values()。类似的,- dict.viewitems(),- dict.viewkeys()和- dict.viewvalues()会分别转换成- dict.items(),- dict.keys()和- dict.values()。另外也会将原有的- dict.items(),- dict.keys()和- dict.values()方法调用用- list包装一层。
- except¶
- 将 - except X, T转换为- except X as T。
- funcattrs¶
- 修复已经重命名的函数属性。比如 - my_function.func_closure会被转换为- my_function.__closure__。
- future¶
- 移除 - from __future__ import new_feature语句。
- getcwdu¶
- 将 - os.getcwdu()重命名为- os.getcwd()。
- has_key¶
- 将 - dict.has_key(key)转换为- key in dict。
- idioms¶
- 这是一个可选的修复器,会进行多种转换,将 Python 代码变成更加常见的写法。类似 - type(x) is SomeClass和- type(x) == SomeClass的类型对比会被转换成- isinstance(x, SomeClass)。- while 1转换成- while True。这个修复器还会在合适的地方使用- sorted()函数。举个例子,这样的代码块:- L = list(some_iterable) L.sort() - 会被转换为: - L = sorted(some_iterable) 
- import¶
- 检测 sibling imports,并将其转换成相对 import。 
- imports¶
- 处理标准库模块的重命名。 
- input¶
- 将 - input(prompt)转换为- eval(input(prompt))。
- intern¶
- 将 - intern()转换为- sys.intern()。
- isinstance¶
- 修复 - isinstance()函数第二个实参中重复的类型。举例来说,- isinstance(x, (int, int))会转换为- isinstance(x, int),- isinstance(x, (int, float, int))会转换为- isinstance(x, (int, float))。
- itertools_imports¶
- 移除 - itertools.ifilter(),- itertools.izip()以及- itertools.imap()的 import。对- itertools.ifilterfalse()的 import 也会替换成- itertools.filterfalse()。
- itertools¶
- 修改 - itertools.ifilter(),- itertools.izip()和- itertools.imap()的调用为对应的内建实现。- itertools.ifilterfalse()会替换成- itertools.filterfalse()。
- metaclass¶
- 将老的元类语法(类体中的 - __metaclass__ = Meta)替换为新的(- class X(metaclass=Meta))。
- methodattrs¶
- 修复老的方法属性名。例如 - meth.im_func会被转换为- meth.__func__。
- ne¶
- 转换老的不等语法,将 - <>转为- !=。
- next¶
- 将迭代器的 - next()方法调用转为- next()函数。也会将- next()方法重命名为- __next__()。
- nonzero¶
- 将被调用的方法定义 - __nonzero__()改名为- __bool__()。
- numliterals¶
- 将八进制字面量转为新的语法。 
- operator¶
- 将 - operator模块中的许多方法调用转为其他的等效函数调用。如果有需要,会添加适当的- import语句,比如- import collections.abc。有以下转换映射:- 从 - 到 - operator.isCallable(obj)- callable(obj)- operator.sequenceIncludes(obj)- operator.contains(obj)- operator.isSequenceType(obj)- isinstance(obj, collections.abc.Sequence)- operator.isMappingType(obj)- isinstance(obj, collections.abc.Mapping)- operator.isNumberType(obj)- isinstance(obj, numbers.Number)- operator.repeat(obj, n)- operator.mul(obj, n)- operator.irepeat(obj, n)- operator.imul(obj, n)
- paren¶
- 在列表生成式中增加必须的括号。例如将 - [x for x in 1, 2]转换为- [x for x in (1, 2)]。
- raise¶
- 将 - raise E, V转换为- raise E(V),将- raise E, V, T转换为- raise E(V).with_traceback(T)。如果- E是元组,这样的转换是不正确的,因为用元组代替异常的做法在 3.0 中已经移除了。
- reduce¶
- 将 - reduce()转换为- functools.reduce()。
- reload¶
- 将 - reload()转换为- importlib.reload()。
- renames¶
- 将 - sys.maxint转换为- sys.maxsize。
- sys_exc¶
- 将弃用的 - sys.exc_value,- sys.exc_type,- sys.exc_traceback替换为- sys.exc_info()的用法。
- throw¶
- 修复生成器的 - throw()方法的 API 变更。
- tuple_params¶
- 移除隐式的元组参数解包。这个修复器会插入临时变量。 
- ws_comma¶
- 移除逗号分隔的元素之间多余的空白。这个修复器是可选的。 
- xreadlines¶
- 将 - for x in file.xreadlines()转换为- for x in file。
lib2to3 --- 2to3 的库¶
源代码: Lib/lib2to3/
Deprecated since version 3.11, will be removed in version 3.13: Python 3.9 已切换至 PEG 解析器 (参见 PEP 617) 而 lib2to3 是使用不够灵活的 LL(1) 解析器。 Python 3.10 包括了 lib2to3 的 LL(1) 解析器所无法解析的新增语言特性 (参见 PEP 634)。 lib2to3 模块已在 Python 3.9 中被标记为即将弃用 (在导入时会引发 PendingDeprecationWarning) 并会在 Python 3.11 中被正式弃用 (会引发 DeprecationWarning)。 它将在 Python 3.13 中被移出标准库。 请考虑使用 LibCST 或 parso 等第三方替代品。
备注
lib2to3 API 并不稳定,并可能在未来大幅修改。