2to3 — 自動化 Python 2 到 3 程式碼轉換

2to3 是一個 Python 程式,它讀取 Python 2.x 原始碼並應用一系列修復程式將其轉換為有效的 Python 3.x 程式碼。標準庫包含一組豐富的修復程式,幾乎可以處理所有程式碼。2to3 支援庫 lib2to3 然而,是一個靈活且通用的庫,因此可以為 2to3 編寫自己的修復程式。

自 3.11 版棄用,將在 3.13 版中移除: lib2to3 模組在 Python 3.9 中標記為待棄用(匯入時引發 PendingDeprecationWarning)並在 Python 3.11 中完全棄用(引發 DeprecationWarning)。2to3 工具是其中的一部分。它將在 Python 3.13 中移除。

使用 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

將列印與原始原始檔相比的差異。2to3 還可以將所需的修改直接寫入原始檔。(除非還給出了 -n,否則將備份原始檔案。)使用 -w 標誌啟用將更改寫回

$ 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 無法自動修復。在這種情況下,2to3 會在檔案的 diff 下方列印警告。你應該解決警告以獲得相容的 3.x 程式碼。

2to3 還可以重構 doctest。要啟用此模式,請使用 -d 標誌。請注意,重構 doctest。這也並不需要模組是有效的 Python。例如,reST 文件中的類似 doctest 的示例也可以使用此選項進行重構。

-v 選項啟用輸出有關翻譯過程的更多資訊。

由於某些 print 語句可以解析為函式呼叫或語句,因此 2to3 並不總是可以讀取包含 print 函式的檔案。當 2to3 檢測到 from __future__ import print_function 編譯器指令的存在時,它會修改其內部語法以將 print() 解釋為函式。此更改也可以使用 -p 標誌手動啟用。使用 -p 對其 print 語句已轉換的程式碼執行修復程式。此外, -e 可用於使 exec() 成為函式。

-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 的 API 類似,但並不完全相同。

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

檢測同級匯入並將其轉換為相對匯入。

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() 的匯入。 itertools.ifilterfalse() 的匯入也已更改為 itertools.filterfalse()

itertools

更改對 itertools.ifilter()itertools.izip()itertools.imap() 的使用,使其等效於其內建函式。將 itertools.ifilterfalse() 更改為 itertools.filterfalse()

long

long 重新命名為 int

map

map() 封裝在 list 呼叫中。還將 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 建構函式。此修復程式是可選的。

standarderror

StandardError 重新命名為 Exception

sys_exc

將已棄用的 sys.exc_valuesys.exc_typesys.exc_traceback 更改為使用 sys.exc_info()

throw

修復生成器的 throw() 方法中的 API 更改。

tuple_params

刪除隱式元組引數解包。此修復程式插入臨時變數。

types

修復 types 模組中刪除某些成員後導致的程式碼中斷。

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/


自 3.11 版本棄用,將在 3.13 版本中移除: Python 3.9 切換到了 PEG 解析器(參見 PEP 617),而 lib2to3 則使用靈活性較低的 LL(1) 解析器。Python 3.10 包含 lib2to3 的 LL(1) 解析器無法解析的新語言語法(參見 PEP 634)。lib2to3 模組在 Python 3.9 中被標記為待棄用(在匯入時引發 PendingDeprecationWarning),並在 Python 3.11 中完全棄用(引發 DeprecationWarning)。它將在 Python 3.13 中從標準庫中移除。考慮使用第三方替代方案,例如 LibCSTparso

注意

lib2to3 API 應被視為不穩定,未來可能會發生巨大變化。