2to3 - 自动将 Python 2 代码转为 Python 3 代码
2to3 是一个 Python 程序,它可以用来读取 Python 2.x 版本的代码,并使用一系列的 修复器 来将其转换为合法的 Python 3.x 代码。标准库中已经包含了丰富的修复器,这足以处理绝大多数代码。不过 2to3 的支持库 lib2to3
是一个很灵活通用的库,所以你也可以为 2to3 编写你自己的修复器。lib2to3
也可以用在那些需要自动处理 Python 代码的应用中。
使用 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 语句的代码运行修复器。
-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)
。- 将已弃用的
unittest
方法替换为正确的。
Python 2.x
Python 3.x
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)
basestring
将
basestring
转换为str
。将
buffer
转换为memoryview
。这个修复器是可选的,因为memoryview
API 和buffer
很相似,但不完全一样。修复字典迭代方法。
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 X, T
转换为except X as T
。将
exec
语句转换为exec()
函数调用。移除
execfile()
的使用。execfile()
的实参会使用open()
,compile()
和exec()
包装。将对
sys.exitfunc
的赋值改为使用atexit
模块代替。修复已经重命名的函数属性。比如
myfunction.funcclosure
会被转换为my_function.__closure
。移除
from future import new_feature
语句。将
os.getcwdu()
重命名为os.getcwd()
。将
dict.has_key(key)
转换为key in dict
。- 这是一个可选的修复器,会进行多种转换,将 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(prompt)
转换为eval(input(prompt))
。将
intern()
转换为sys.intern()
。修复
isinstance()
函数第二个实参中重复的类型。举例来说,isinstance(x, (int, int))
会转换为isinstance(x, int)
,isinstance(x, (int, float, int))
会转换为isinstance(x, (int, float))
。移除
itertools.ifilter()
,itertools.izip()
以及itertools.imap()
的 import。对itertools.ifilterfalse()
的 import 也会替换成itertools.filterfalse()
。修改
itertools.ifilter()
,itertools.izip()
和itertools.imap()
的调用为对应的内建实现。itertools.ifilterfalse()
会替换成itertools.filterfalse()
。将
long
重命名为int
。用
list
包装map()
。同时也会将map(None, x)
替换为list(x)
。使用from future_builtins import map
禁用这个修复器。将老的元类语法(类体中的
metaclass = Meta
)替换为新的(class X(metaclass=Meta)
)。修复老的方法属性名。例如
meth.imfunc
会被转换为meth._func
。转换老的不等语法,将
<>
转为!=
。将
nonzero()
转换为bool()
。将八进制字面量转为新的语法。
- 将
operator
模块中的许多方法调用转为其他的等效函数调用。如果有需要,会添加适当的import
语句,比如import collections.abc
。有以下转换映射:
Python 2.x
Python 3.x
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()
函数。将
raise E, V
转换为raise E(V)
,将raise E, V, T
转换为raise E(V).with_traceback(T)
。如果E
是元组,这样的转换是不正确的,因为用元组代替异常的做法在 3.0 中已经移除了。将
raw_input()
转换为input()
。将
reduce()
转换为functools.reduce()
。将
reload()
转换为importlib.reload()
。将
sys.maxint
转换为sys.maxsize
。将反引号 repr 表达式替换为
repr()
函数。将
set
构造函数替换为 set literals 写法。这个修复器是可选的。将
StandardError
重命名为Exception
。将弃用的
sys.exc_value
,sys.exc_type
,sys.exc_traceback
替换为sys.exc_info()
的用法。修复生成器的
throw()
方法的 API 变更。移除隐式的元组参数解包。这个修复器会插入临时变量。
修复
type
模块中一些成员的移除引起的代码问题。将
unicode
重命名为str
。移除逗号分隔的元素之间多余的空白。这个修复器是可选的。
将
for x in file.xreadlines()
转换为for x in file
。- 用
list
包装zip()
。如果使用了from future_builtins import zip
的话会禁用。
lib2to3 —— 2to3 支持库
源代码:Lib/lib2to3/
注解
lib2to3
API 并不稳定,并可能在未来大幅修改。