2to3 - 自動(dòng)將 Python 2 代碼轉為 Python 3 代碼?
2to3 是一個(gè) Python 程序,它可以讀取 Python 2.x 的源代碼并使用一系列的 修復器 來(lái)將其轉換為合法的 Python 3.x 代碼。 標準庫已包含了豐富的修復器,這足以處理幾乎所有代碼。 不過(guò) 2to3 的支持庫 lib2to3
是一個(gè)很靈活通用的庫,所以還可以編寫(xiě)你自己的 2to3 修復器。
Deprecated since version 3.11, will be removed in version 3.13: The lib2to3
module was marked pending for deprecation in Python 3.9
(raising PendingDeprecationWarning
on import) and fully deprecated
in Python 3.11 (raising DeprecationWarning
). The 2to3
tool is
part of that. It will be removed in Python 3.13.
使用 2to3?
2to3 通常會(huì )作為腳本和 Python 解釋器一起安裝,你可以在 Python 根目錄的 Tools/scripts
文件夾下找到它。
2to3 的基本調用參數是一個(gè)需要轉換的文件或目錄列表。對于目錄,會(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 轉換成 Python 3.x 版本的代碼:
$ 2to3 example.py
這個(gè)命令會(huì )打印出和源文件的區別。通過(guò)傳入 -w
參數,2to3 也可以把需要的修改寫(xiě)回到原文件中(除非傳入了 -n
參數,否則會(huì )為原始文件創(chuàng )建一個(gè)副本):
$ 2to3 -w example.py
在轉換完成后,example.py
看起來(lái)像是這樣:
def greet(name):
print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name)
注釋和縮進(jìn)都會(huì )在轉換過(guò)程中保持不變。
默認情況下,2to3 會(huì )執行 預定義修復器 的集合。使用 -l
參數可以列出所有可用的修復器。使用 -f
參數可以明確指定需要使用的修復器集合。而使用 -x
參數則可以明確指定不使用的修復器。下面的例子會(huì )只使用 imports
和 has_key
修復器運行:
$ 2to3 -f imports -f has_key example.py
這個(gè)命令會(huì )執行除了 apply
之外的所有修復器:
$ 2to3 -x apply example.py
有一些修復器是需要 顯式指定 的,它們默認不會(huì )執行,必須在命令行中列出才會(huì )執行。比如下面的例子,除了默認的修復器以外,還會(huì )執行 idioms
修復器:
$ 2to3 -f all -f idioms example.py
注意這里使用 all
來(lái)啟用所有默認的修復器。
有些情況下 2to3 會(huì )找到源碼中有一些需要修改,但是無(wú)法自動(dòng)處理的代碼。在這種情況下,2to3 會(huì )在差異處下面打印一個(gè)警告信息。你應該定位到相應的代碼并對其進(jìn)行修改,以使其兼容 Python 3.x。
2to3 也可以重構 doctests。使用 -d
開(kāi)啟這個(gè)模式。需要注意*只有* doctests 會(huì )被重構。這種模式下不需要文件是合法的 Python 代碼。舉例來(lái)說(shuō),reST 文檔中類(lèi)似 doctests 的示例也可以使用這個(gè)選項進(jìn)行重構。
-v
選項可以輸出更多轉換程序的詳細信息。
由于某些 print 語(yǔ)句可被解讀為函數調用或是語(yǔ)句,2to3 并不總能讀取包含 print 函數的文件。 當 2to3 檢測到存在 from __future__ import print_function
編譯器指令時(shí),會(huì )修改其內部語(yǔ)法將 print()
解讀為函數。 這一變動(dòng)也可以使用 -p
旗標手動(dòng)開(kāi)啟。 使用 -p
來(lái)為已轉換過(guò) print 語(yǔ)句的代碼運行修復器。 也可以使用 -e
將 exec()
解讀為函數。
-o
或 --output-dir
選項可以指定將轉換后的文件寫(xiě)入其他目錄中。由于這種情況下不會(huì )覆寫(xiě)原始文件,所以創(chuàng )建副本文件毫無(wú)意義,因此也需要使用 -n
選項來(lái)禁用創(chuàng )建副本。
3.2.3 新版功能: 增加了 -o
選項。
-W
或 --write-unchanged-files
選項用來(lái)告訴 2to3 始終需要輸出文件,即使沒(méi)有任何改動(dòng)。這在使用 -o
參數時(shí)十分有用,這樣就可以將整個(gè) Python 源碼包完整地轉換到另一個(gè)目錄。這個(gè)選項隱含了 -w
選項,否則等于沒(méi)有作用。
3.2.3 新版功能: 增加了 -W
選項。
--add-suffix
選項接受一個(gè)字符串,用來(lái)作為后綴附加在輸出文件名后面的后面。由于寫(xiě)入的文件名與原始文件不同,所以沒(méi)有必要創(chuàng )建副本,因此 -n
選項也是必要的。舉個(gè)例子:
$ 2to3 -n -W --add-suffix=3 example.py
這樣會(huì )把轉換后的文件寫(xiě)入 example.py3
文件。
3.2.3 新版功能: 增加了 --add-suffix
選項。
將整個(gè)項目從一個(gè)目錄轉換到另一個(gè)目錄可以用這樣的命令:
$ 2to3 --output-dir=python3-version/mycode -W -n python2-version/mycode
修復器?
轉換代碼的每一個(gè)步驟都封裝在修復器中??梢允褂?2to3 -l
來(lái)列出可用的修復器。之前已經(jīng)提到,每個(gè)修復器都可以獨立地打開(kāi)或是關(guān)閉。下面會(huì )對各個(gè)修復器做更詳細的描述。
- apply?
移除對
apply()
的使用,舉例來(lái)說(shuō),apply(function, *args, **kwargs)
會(huì )被轉換成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
。這個(gè)修復器是可選的,因為memoryview
API 和buffer
很相似,但不完全一樣。
- dict?
修復字典迭代方法。
dict.iteritems()
會(huì )轉換成dict.items()
,dict.iterkeys()
會(huì )轉換成dict.keys()
,dict.itervalues()
會(huì )轉換成dict.values()
。類(lèi)似的,dict.viewitems()
,dict.viewkeys()
和dict.viewvalues()
會(huì )分別轉換成dict.items()
,dict.keys()
和dict.values()
。另外也會(huì )將原有的dict.items()
,dict.keys()
和dict.values()
方法調用用list
包裝一層。
- except?
將
except X, T
轉換為except X as T
。
- funcattrs?
修復已經(jīng)重命名的函數屬性。比如
my_function.func_closure
會(huì )被轉換為my_function.__closure__
。
- future?
移除
from __future__ import new_feature
語(yǔ)句。
- getcwdu?
將
os.getcwdu()
重命名為os.getcwd()
。
- has_key?
將
dict.has_key(key)
轉換為key in dict
。
- idioms?
這是一個(gè)可選的修復器,會(huì )進(jìn)行多種轉換,將 Python 代碼變成更加常見(jiàn)的寫(xiě)法。類(lèi)似
type(x) is SomeClass
和type(x) == SomeClass
的類(lèi)型對比會(huì )被轉換成isinstance(x, SomeClass)
。while 1
轉換成while True
。這個(gè)修復器還會(huì )在合適的地方使用sorted()
函數。舉個(gè)例子,這樣的代碼塊:L = list(some_iterable) L.sort()
會(huì )被轉換為:
L = sorted(some_iterable)
- import?
檢測 sibling imports,并將其轉換成相對 import。
- imports?
處理標準庫模塊的重命名。
- input?
將
input(prompt)
轉換為eval(input(prompt))
。
- intern?
將
intern()
轉換為sys.intern()
。
- isinstance?
修復
isinstance()
函數第二個(gè)實(shí)參中重復的類(lèi)型。舉例來(lái)說(shuō),isinstance(x, (int, int))
會(huì )轉換為isinstance(x, int)
,isinstance(x, (int, float, int))
會(huì )轉換為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()
的調用為對應的內建實(shí)現。itertools.ifilterfalse()
會(huì )替換成itertools.filterfalse()
。
- map?
用
list
包裝map()
。同時(shí)也會(huì )將map(None, x)
替換為list(x)
。使用from future_builtins import map
禁用這個(gè)修復器。
- metaclass?
將老的元類(lèi)語(yǔ)法(類(lèi)體中的
__metaclass__ = Meta
)替換為新的(class X(metaclass=Meta)
)。
- methodattrs?
修復老的方法屬性名。例如
meth.im_func
會(huì )被轉換為meth.__func__
。
- ne?
轉換老的不等語(yǔ)法,將
<>
轉為!=
。
- next?
將迭代器的
next()
方法調用轉為next()
函數。也會(huì )將next()
方法重命名為__next__()
。
- nonzero?
Renames definitions of methods called
__nonzero__()
to__bool__()
.
- numliterals?
將八進(jìn)制字面量轉為新的語(yǔ)法。
- operator?
將
operator
模塊中的許多方法調用轉為其他的等效函數調用。如果有需要,會(huì )添加適當的import
語(yǔ)句,比如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 中已經(jīng)移除了。
- 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?
移除隱式的元組參數解包。這個(gè)修復器會(huì )插入臨時(shí)變量。
- ws_comma?
移除逗號分隔的元素之間多余的空白。這個(gè)修復器是可選的。
- xreadlines?
將
for x in file.xreadlines()
轉換為for x in file
。
lib2to3
—— 2to3 支持庫?
源代碼: Lib/lib2to3/
Deprecated since version 3.11, will be removed in version 3.13: Python 3.9 switched to a PEG parser (see PEP 617) while lib2to3 is
using a less flexible LL(1) parser. Python 3.10 includes new language
syntax that is not parsable by lib2to3's LL(1) parser (see PEP 634).
The lib2to3
module was marked pending for deprecation in Python 3.9
(raising PendingDeprecationWarning
on import) and fully deprecated
in Python 3.11 (raising DeprecationWarning
).
It will be removed from the standard library in Python 3.13.
Consider third-party alternatives such as LibCST or parso.
備注
lib2to3
API 并不穩定,并可能在未來(lái)大幅修改。