2to3 - 自動(dòng)將 Python 2 代碼轉(zhuǎn)為 Python 3 代碼?
2to3 是一個(gè) Python 程序,它可以用來讀取 Python 2.x 版本的代碼,并使用一系列的 修復(fù)器 來將其轉(zhuǎn)換為合法的 Python 3.x 代碼。標(biāo)準(zhǔn)庫中已經(jīng)包含了豐富的修復(fù)器,這足以處理絕大多數(shù)代碼。不過 2to3 的支持庫 lib2to3 是一個(gè)很靈活通用的庫,所以你也可以為 2to3 編寫你自己的修復(fù)器。lib2to3 也可以用在那些需要自動(dòng)處理 Python 代碼的應(yīng)用中。
使用 2to3?
2to3 通常會(huì)作為腳本和 Python 解釋器一起安裝,你可以在 Python 根目錄的 Tools/scripts 文件夾下找到它。
2to3 的基本調(diào)用參數(shù)是一個(gè)需要轉(zhuǎn)換的文件或目錄列表。對于目錄,會(huì)遞歸地尋找其中的 Python 源碼。
這里有一個(gè) Python 2.x 的源碼文件,example.py:
def greet(name):
print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)
它可以在命令行中使用 2to3 轉(zhuǎn)換成 Python 3.x 版本的代碼:
$ 2to3 example.py
這個(gè)命令會(huì)打印出和源文件的區(qū)別。通過傳入 -w 參數(shù),2to3 也可以把需要的修改寫回到原文件中(除非傳入了 -n 參數(shù),否則會(huì)為原始文件創(chuàng)建一個(gè)副本):
$ 2to3 -w example.py
在轉(zhuǎn)換完成后,example.py 看起來像是這樣:
def greet(name):
print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name)
注釋和縮進(jìn)都會(huì)在轉(zhuǎn)換過程中保持不變。
默認(rèn)情況下,2to3 會(huì)執(zhí)行 預(yù)定義修復(fù)器 的集合。使用 -l 參數(shù)可以列出所有可用的修復(fù)器。使用 -f 參數(shù)可以明確指定需要使用的修復(fù)器集合。而使用 -x 參數(shù)則可以明確指定不使用的修復(fù)器。下面的例子會(huì)只使用 imports 和 has_key 修復(fù)器運(yùn)行:
$ 2to3 -f imports -f has_key example.py
這個(gè)命令會(huì)執(zhí)行除了 apply 之外的所有修復(fù)器:
$ 2to3 -x apply example.py
有一些修復(fù)器是需要 顯式指定 的,它們默認(rèn)不會(huì)執(zhí)行,必須在命令行中列出才會(huì)執(zhí)行。比如下面的例子,除了默認(rèn)的修復(fù)器以外,還會(huì)執(zhí)行 idioms 修復(fù)器:
$ 2to3 -f all -f idioms example.py
注意這里使用 all 來啟用所有默認(rèn)的修復(fù)器。
有些情況下 2to3 會(huì)找到源碼中有一些需要修改,但是無法自動(dòng)處理的代碼。在這種情況下,2to3 會(huì)在差異處下面打印一個(gè)警告信息。你應(yīng)該定位到相應(yīng)的代碼并對其進(jìn)行修改,以使其兼容 Python 3.x。
2to3 也可以重構(gòu) doctests。使用 -d 開啟這個(gè)模式。需要注意*只有* doctests 會(huì)被重構(gòu)。這種模式下不需要文件是合法的 Python 代碼。舉例來說,reST 文檔中類似 doctests 的示例也可以使用這個(gè)選項(xiàng)進(jìn)行重構(gòu)。
-v 選項(xiàng)可以輸出更多轉(zhuǎn)換程序的詳細(xì)信息。
由于某些 print 語句可被解讀為函數(shù)調(diào)用或是語句,2to3 并不是總能讀取包含 print 函數(shù)的文件。當(dāng) 2to3 檢測到存在 from __future__ import print_function 編譯器指令時(shí),會(huì)修改其內(nèi)部語法將 print() 解讀為函數(shù)。這一變動(dòng)也可以使用 -p 選項(xiàng)手動(dòng)開啟。使用 -p 來為已經(jīng)轉(zhuǎn)換過 print 語句的代碼運(yùn)行修復(fù)器。
-o 或 --output-dir 選項(xiàng)可以指定將轉(zhuǎn)換后的文件寫入其他目錄中。由于這種情況下不會(huì)覆寫原始文件,所以創(chuàng)建副本文件毫無意義,因此也需要使用 -n 選項(xiàng)來禁用創(chuàng)建副本。
3.2.3 新版功能: 增加了 -o 選項(xiàng)。
-W 或 --write-unchanged-files 選項(xiàng)用來告訴 2to3 始終需要輸出文件,即使沒有任何改動(dòng)。這在使用 -o 參數(shù)時(shí)十分有用,這樣就可以將整個(gè) Python 源碼包完整地轉(zhuǎn)換到另一個(gè)目錄。這個(gè)選項(xiàng)隱含了 -w 選項(xiàng),否則等于沒有作用。
3.2.3 新版功能: 增加了 -W 選項(xiàng)。
--add-suffix 選項(xiàng)接受一個(gè)字符串,用來作為后綴附加在輸出文件名后面的后面。由于寫入的文件名與原始文件不同,所以沒有必要?jiǎng)?chuàng)建副本,因此 -n 選項(xiàng)也是必要的。舉個(gè)例子:
$ 2to3 -n -W --add-suffix=3 example.py
這樣會(huì)把轉(zhuǎn)換后的文件寫入 example.py3 文件。
3.2.3 新版功能: 增加了 --add-suffix 選項(xiàng)。
將整個(gè)項(xiàng)目從一個(gè)目錄轉(zhuǎn)換到另一個(gè)目錄可以用這樣的命令:
$ 2to3 --output-dir=python3-version/mycode -W -n python2-version/mycode
修復(fù)器?
轉(zhuǎn)換代碼的每一個(gè)步驟都封裝在修復(fù)器中??梢允褂?2to3 -l 來列出可用的修復(fù)器。之前已經(jīng)提到,每個(gè)修復(fù)器都可以獨(dú)立地打開或是關(guān)閉。下面會(huì)對各個(gè)修復(fù)器做更詳細(xì)的描述。
-
apply? 移除對
apply()的使用,舉例來說,apply(function, *args, **kwargs)會(huì)被轉(zhuǎn)換成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轉(zhuǎn)換為memoryview。這個(gè)修復(fù)器是可選的,因?yàn)?memoryviewAPI 和buffer很相似,但不完全一樣。
-
dict? 修復(fù)字典迭代方法。
dict.iteritems()會(huì)轉(zhuǎn)換成dict.items(),dict.iterkeys()會(huì)轉(zhuǎn)換成dict.keys(),dict.itervalues()會(huì)轉(zhuǎn)換成dict.values()。類似的,dict.viewitems(),dict.viewkeys()和dict.viewvalues()會(huì)分別轉(zhuǎn)換成dict.items(),dict.keys()和dict.values()。另外也會(huì)將原有的dict.items(),dict.keys()和dict.values()方法調(diào)用用list包裝一層。
-
except? 將
except X, T轉(zhuǎn)換為except X as T。
-
funcattrs? 修復(fù)已經(jīng)重命名的函數(shù)屬性。比如
my_function.func_closure會(huì)被轉(zhuǎn)換為my_function.__closure__。
-
future? 移除
from __future__ import new_feature語句。
-
getcwdu? 將
os.getcwdu()重命名為os.getcwd()。
-
has_key? 將
dict.has_key(key)轉(zhuǎn)換為key in dict。
-
idioms? 這是一個(gè)可選的修復(fù)器,會(huì)進(jìn)行多種轉(zhuǎn)換,將 Python 代碼變成更加常見的寫法。類似
type(x) is SomeClass和type(x) == SomeClass的類型對比會(huì)被轉(zhuǎn)換成isinstance(x, SomeClass)。while 1轉(zhuǎn)換成while True。這個(gè)修復(fù)器還會(huì)在合適的地方使用sorted()函數(shù)。舉個(gè)例子,這樣的代碼塊:L = list(some_iterable) L.sort()
會(huì)被轉(zhuǎn)換為:
L = sorted(some_iterable)
-
import? 檢測 sibling imports,并將其轉(zhuǎn)換成相對 import。
-
imports? 處理標(biāo)準(zhǔn)庫模塊的重命名。
-
input? 將
input(prompt)轉(zhuǎn)換為eval(input(prompt))。
-
intern? 將
intern()轉(zhuǎn)換為sys.intern()。
-
isinstance? 修復(fù)
isinstance()函數(shù)第二個(gè)實(shí)參中重復(fù)的類型。舉例來說,isinstance(x, (int, int))會(huì)轉(zhuǎn)換為isinstance(x, int),isinstance(x, (int, float, int))會(huì)轉(zhuǎn)換為isinstance(x, (int, float))。
-
itertools_imports? 移除
itertools.ifilter(),itertools.izip()以及itertools.imap()的 import。對itertools.ifilterfalse()的 import 也會(huì)替換成itertools.filterfalse()。
-
itertools? 修改
itertools.ifilter(),itertools.izip()和itertools.imap()的調(diào)用為對應(yīng)的內(nèi)建實(shí)現(xiàn)。itertools.ifilterfalse()會(huì)替換成itertools.filterfalse()。
-
map? 用
list包裝map()。同時(shí)也會(huì)將map(None, x)替換為list(x)。使用from future_builtins import map禁用這個(gè)修復(fù)器。
-
metaclass? 將老的元類語法(類體中的
__metaclass__ = Meta)替換為新的(class X(metaclass=Meta))。
-
methodattrs? 修復(fù)老的方法屬性名。例如
meth.im_func會(huì)被轉(zhuǎn)換為meth.__func__。
-
ne? 轉(zhuǎn)換老的不等語法,將
<>轉(zhuǎn)為!=。
-
next? 將迭代器的
next()方法調(diào)用轉(zhuǎn)為next()函數(shù)。也會(huì)將next()方法重命名為__next__()。
-
nonzero? 將
__nonzero__()轉(zhuǎn)換為__bool__()。
-
numliterals? 將八進(jìn)制字面量轉(zhuǎn)為新的語法。
-
operator? 將
operator模塊中的許多方法調(diào)用轉(zhuǎn)為其他的等效函數(shù)調(diào)用。如果有需要,會(huì)添加適當(dāng)?shù)?import語句,比如import collections.abc。有以下轉(zhuǎn)換映射:從
到
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]轉(zhuǎn)換為[x for x in (1, 2)]。
-
raise? 將
raise E, V轉(zhuǎn)換為raise E(V),將raise E, V, T轉(zhuǎn)換為raise E(V).with_traceback(T)。如果E是元組,這樣的轉(zhuǎn)換是不正確的,因?yàn)橛迷M代替異常的做法在 3.0 中已經(jīng)移除了。
-
reduce? 將
reduce()轉(zhuǎn)換為functools.reduce()。
-
reload? 將
reload()轉(zhuǎn)換為importlib.reload()。
-
renames? 將
sys.maxint轉(zhuǎn)換為sys.maxsize。
-
sys_exc? 將棄用的
sys.exc_value,sys.exc_type,sys.exc_traceback替換為sys.exc_info()的用法。
-
throw? 修復(fù)生成器的
throw()方法的 API 變更。
-
tuple_params? 移除隱式的元組參數(shù)解包。這個(gè)修復(fù)器會(huì)插入臨時(shí)變量。
-
ws_comma? 移除逗號分隔的元素之間多余的空白。這個(gè)修復(fù)器是可選的。
-
xreadlines? 將
for x in file.xreadlines()轉(zhuǎn)換為for x in file。
