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)。
-
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。這個修復器是可選的,因為memoryviewAPI 和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。
-
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? 檢測 sibling imports,并將其轉換成相對 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()的 import。對itertools.ifilterfalse()的 import 也會替換成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。
