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ì)只使用 importshas_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)

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 轉(zhuǎn)換為 str。

buffer?

buffer 轉(zhuǎn)換為 memoryview。這個(gè)修復(fù)器是可選的,因?yàn)?memoryview API 和 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。

exec?

exec 語句轉(zhuǎn)換為 exec() 函數(shù)調(diào)用。

execfile?

移除 execfile() 的使用。execfile() 的實(shí)參會(huì)使用 open(),compile()exec() 包裝。

exitfunc?

將對 sys.exitfunc 的賦值改為使用 atexit 模塊代替。

filter?

filter() 函數(shù)用 list 包裝一層。

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 SomeClasstype(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)庫模塊的重命名。

imports2?

處理標(biāo)準(zhǔn)庫中其他模塊的重命名。這個(gè)修復(fù)器由于一些技術(shù)上的限制,因此和 imports 拆分開了。

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()

long?

long 重命名為 int。

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)]。

print?

print 語句轉(zhuǎn)換為 print() 函數(shù)。

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)移除了。

raw_input?

raw_input() 轉(zhuǎn)換為 input()。

reduce?

reduce() 轉(zhuǎn)換為 functools.reduce()。

reload?

reload() 轉(zhuǎn)換為 importlib.reload()。

renames?

sys.maxint 轉(zhuǎn)換為 sys.maxsize。

repr?

將反引號 repr 表達(dá)式替換為 repr() 函數(shù)。

set_literal?

set 構(gòu)造函數(shù)替換為 set literals 寫法。這個(gè)修復(fù)器是可選的。

standarderror?

StandardError 重命名為 Exception

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í)變量。

types?

修復(fù) type 模塊中一些成員的移除引起的代碼問題。

unicode?

unicode 重命名為 str。

urllib?

urlliburllib2 重命名為 urllib 包。

ws_comma?

移除逗號分隔的元素之間多余的空白。這個(gè)修復(fù)器是可選的。

xrange?

xrange() 重命名為 range(),并用 list 包裝原有的 range()。

xreadlines?

for x in file.xreadlines() 轉(zhuǎn)換為 for x in file

zip?

list 包裝 zip()。如果使用了 from future_builtins import zip 的話會(huì)禁用。

lib2to3 —— 2to3 支持庫?

源代碼: Lib/lib2to3/


注解

lib2to3 API 并不穩(wěn)定,并可能在未來大幅修改。