• 2to3 - 自动将 Python 2 代码转为 Python 3 代码
    • 使用 2to3
    • 修复器
    • lib2to3 —— 2to3 支持库

    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

    1. def greet(name):
    2. print "Hello, {0}!".format(name)
    3. print "What's your name?"
    4. name = raw_input()
    5. greet(name)

    它可以在命令行中使用 2to3 转换成 Python 3.x 版本的代码:

    1. $ 2to3 example.py

    这个命令会打印出和源文件的区别。通过传入 -w 参数,2to3 也可以把需要的修改写回到原文件中(除非传入了 -n 参数,否则会为原始文件创建一个副本):

    1. $ 2to3 -w example.py

    在转换完成后,example.py 看起来像是这样:

    1. def greet(name):
    2. print("Hello, {0}!".format(name))
    3. print("What's your name?")
    4. name = input()
    5. greet(name)

    注释和缩进都会在转换过程中保持不变。

    默认情况下,2to3 会执行 预定义修复器 的集合。使用 -l 参数可以列出所有可用的修复器。使用 -f 参数可以明确指定需要使用的修复器集合。而使用 -x 参数则可以明确指定不使用的修复器。下面的例子会只使用 importshas_key 修复器运行:

    1. $ 2to3 -f imports -f has_key example.py

    这个命令会执行除了 apply 之外的所有修复器:

    1. $ 2to3 -x apply example.py

    有一些修复器是需要 显式指定 的,它们默认不会执行,必须在命令行中列出才会执行。比如下面的例子,除了默认的修复器以外,还会执行 idioms 修复器:

    1. $ 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 选项也是必要的。举个例子:

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

    这样会把转换后的文件写入 example.py3 文件。

    3.2.3 新版功能: 增加了 —add-suffix 选项。

    将整个项目从一个目录转换到另一个目录可以用这样的命令:

    1. $ 2to3 --output-dir=python3-version/mycode -W -n python2-version/mycode

    修复器

    转换代码的每一个步骤都封装在修复器中。可以使用 2to3 -l 来列出可用的修复器。之前已经提到,每个修复器都可以独立地打开或是关闭。下面会对各个修复器做更详细的描述。

    • apply
    • 移除对 apply() 的使用,举例来说,apply(function, args, **kwargs) 会被转换成 function(args, **kwargs)

    • asserts

    • 将已弃用的 unittest 方法替换为正确的。

    Python 2.x

    Python 3.x

    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 很相似,但不完全一样。

    • 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

    • 修复已经重命名的函数属性。比如 myfunction.funcclosure 会被转换为 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() 函数。举个例子,这样的代码块:
    1. L = list(some_iterable)
    2. L.sort()

    会被转换为:

    1. L = sorted(some_iterable)
    • import
    • 检测 sibling imports,并将其转换成相对 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() 的 import。对 itertools.ifilterfalse() 的 import 也会替换成 itertools.filterfalse()

    • itertools

    • 修改 itertools.ifilter()itertools.izip()itertools.imap() 的调用为对应的内建实现。itertools.ifilterfalse() 会替换成 itertools.filterfalse()

    • long

    • long 重命名为 int

    • map

    • list 包装 map()。同时也会将 map(None, x) 替换为 list(x)。使用 from future_builtins import map 禁用这个修复器。

    • metaclass

    • 将老的元类语法(类体中的 metaclass = Meta)替换为新的(class X(metaclass=Meta))。

    • methodattrs

    • 修复老的方法属性名。例如 meth.imfunc 会被转换为 meth._func

    • ne

    • 转换老的不等语法,将 <> 转为 !=

    • next

    • 将迭代器的 next() 方法调用转为 next() 函数。也会将 next() 方法重命名为 next()

    • nonzero

    • nonzero() 转换为 bool()

    • numliterals

    • 将八进制字面量转为新的语法。

    • operator

    • 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 语句转换为 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 构造函数替换为 set literals 写法。这个修复器是可选的。

    • standarderror

    • StandardError 重命名为 Exception

    • sys_exc

    • 将弃用的 sys.exc_valuesys.exc_typesys.exc_traceback 替换为 sys.exc_info() 的用法。

    • throw

    • 修复生成器的 throw() 方法的 API 变更。

    • tuple_params

    • 移除隐式的元组参数解包。这个修复器会插入临时变量。

    • types

    • 修复 type 模块中一些成员的移除引起的代码问题。

    • 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/


    注解

    lib2to3 API 并不稳定,并可能在未来大幅修改。