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 參數則可以明確指定不使用的修復器。下面的例子會只使用 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 會在差異處下面打印一個警告信息。你應該定位到相應的代碼并對其進行修改,以使其兼容 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)

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

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?

檢測 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.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 構造函數替換為 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 并不穩定,并可能在未來大幅修改。