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
明确禁用修复程序。以下示例仅运行 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 无法自动修复。在这种情况下,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)
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
。此修复程序是可选的,因为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
。
- 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¶
检测同级导入并将其转换为相对导入。
- 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()
。
- 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/
自 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 中从标准库中移除。考虑使用第三方替代方案,例如 LibCST 或 parso。
注意
lib2to3
API 应被视为不稳定,未来可能会发生巨大变化。