2to3 — 自动化 Python 2 到 3 代码转换

2to3 是一个 Python 程序,它读取 Python 2.x 源代码并应用一系列修复程序将其转换为有效的 Python 3.x 代码。标准库包含一组丰富的修复程序,几乎可以处理所有代码。2to3 支持库 lib2to3 然而,是一个灵活且通用的库,因此可以为 2to3 编写自己的修复程序。

自 3.11 版弃用,将在 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

将打印与原始源文件相比的差异。2to3 还可以将所需的修改直接写入源文件。(除非还给出了 -n,否则将备份原始文件。)使用 -w 标志启用将更改写回

$ 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 明确禁用修复程序。以下示例仅运行 importshas_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 无法自动修复。在这种情况下,2to3 会在文件的 diff 下方打印警告。你应该解决警告以获得兼容的 3.x 代码。

2to3 还可以重构 doctest。要启用此模式,请使用 -d 标志。请注意,重构 doctest。这也并不需要模块是有效的 Python。例如,reST 文档中的类似 doctest 的示例也可以使用此选项进行重构。

-v 选项启用输出有关翻译过程的更多信息。

由于某些 print 语句可以解析为函数调用或语句,因此 2to3 并不总是可以读取包含 print 函数的文件。当 2to3 检测到 from __future__ import print_function 编译器指令的存在时,它会修改其内部语法以将 print() 解释为函数。此更改也可以使用 -p 标志手动启用。使用 -p 对其 print 语句已转换的代码运行修复程序。此外, -e 可用于使 exec() 成为函数。

-o--output-dir 选项允许指定要将处理后的输出文件写入的备用目录。在不覆盖输入文件时,使用此选项时需要 -n 标志,因为备份文件没有意义。

在 3.2.3 版中添加: 添加了 -o 选项。

标志 -W--write-unchanged-files 告知 2to3 始终写入输出文件,即使文件不需要任何更改。这与 -o 结合使用时最有用,这样便可将整个 Python 源代码树从一个目录复制到另一个目录,同时进行转换。此选项隐含标志 -w,否则它没有任何意义。

在版本 3.2.3 中添加: 添加了标志 -W

选项 --add-suffix 指定要追加到所有输出文件名的字符串。指定此选项时需要标志 -n,因为写入不同文件名时不需要备份。示例

$ 2to3 -n -W --add-suffix=3 example.py

将导致写入一个名为 example.py3 的已转换文件。

在版本 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)

assertEqual(a, b)

assertEquals(a, b)

assertEqual(a, b)

failIfEqual(a, b)

assertNotEqual(a, b)

assertNotEquals(a, b)

assertNotEqual(a, b)

failUnless(a)

assertTrue(a)

assert_(a)

assertTrue(a)

failIf(a)

assertFalse(a)

failUnlessRaises(exc, cal)

assertRaises(exc, cal)

failUnlessAlmostEqual(a, b)

assertAlmostEqual(a, b)

assertAlmostEquals(a, b)

assertAlmostEqual(a, b)

failIfAlmostEqual(a, b)

assertNotAlmostEqual(a, b)

assertNotAlmostEquals(a, b)

assertNotAlmostEqual(a, b)

basestring

basestring 转换为 str

buffer

buffer 转换为 memoryview。此修复程序是可选的,因为 memoryview API 与 buffer 的 API 类似,但并不完全相同。

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

exec

exec 语句转换为 exec() 函数。

execfile

删除 execfile() 的用法。 execfile() 的参数被包装在对 open()compile()exec() 的调用中。

exitfunc

sys.exitfunc 的赋值更改为使用 atexit 模块。

filter

filter() 用法包装在 list 调用中。

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 SomeClasstype(x) == SomeClass 等类型比较转换为 isinstance(x, SomeClass)while 1 变成 while True。此修复程序还尝试在适当的位置使用 sorted()。例如,此块

L = list(some_iterable)
L.sort()

将更改为

L = sorted(some_iterable)
import

检测同级导入并将其转换为相对导入。

imports

处理标准库中的模块重命名。

imports2

处理标准库中的其他模块重命名。它与 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() 的导入。 itertools.ifilterfalse() 的导入也已更改为 itertools.filterfalse()

itertools

更改对 itertools.ifilter()itertools.izip()itertools.imap() 的使用,使其等效于其内置函数。将 itertools.ifilterfalse() 更改为 itertools.filterfalse()

long

long 重命名为 int

map

map() 封装在 list 调用中。还将 map(None, x) 更改为 list(x)。使用 from future_builtins import map 禁用此修复程序。

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)]

print

print 语句转换为 print() 函数。

raise

raise E, V 转换为 raise E(V),将 raise E, V, T 转换为 raise E(V).with_traceback(T)。如果 E 是一个元组,则转换将不正确,因为在 3.0 中已删除将元组替换为异常的做法。

raw_input

raw_input() 转换为 input()

reduce

处理 reduce() 移至 functools.reduce()

reload

reload() 转换为 importlib.reload()

renames

sys.maxint 更改为 sys.maxsize

repr

使用 repr() 函数替换反引号 repr。

set_literal

使用集合字面量替换 set 构造函数。此修复程序是可选的。

standarderror

StandardError 重命名为 Exception

sys_exc

将已弃用的 sys.exc_valuesys.exc_typesys.exc_traceback 更改为使用 sys.exc_info()

throw

修复生成器的 throw() 方法中的 API 更改。

tuple_params

删除隐式元组参数解包。此修复程序插入临时变量。

types

修复 types 模块中删除某些成员后导致的代码中断。

unicode

unicode 重命名为 str

urllib

处理将 urlliburllib2 重命名为 urllib 包。

ws_comma

从逗号分隔项中移除多余的空白。此修复程序是可选的。

xrange

xrange() 重命名为 range(),并用 list 包装现有的 range() 调用。

xreadlines

for x in file.xreadlines() 更改为 for x in file

zip

list 调用中包装 zip() 用法。当出现 from future_builtins import zip 时,此功能将被禁用。

lib2to3 — 2to3 的库

源代码: Lib/lib2to3/


自 3.11 版本弃用,将在 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 中从标准库中移除。考虑使用第三方替代方案,例如 LibCSTparso

注意

lib2to3 API 应被视为不稳定,未来可能会发生巨大变化。