python2 與 python3 語法區別
概述#
幾乎所有的Python 2程序都需要一些修改才能正常地運行在Python 3的環境下。為了簡化這個轉換過程,Python 3自帶了一個叫做2to3
的實用腳本(Utility Script),這個腳本會將你的Python 2程序源文件作為輸入,然后自動將其轉換到Python 3的形式。案例研究:將chardet
移植到Python 3(porting chardet to Python 3)描述了如何運行這個腳本,然后展示了一些它不能自動修復的情況。這篇附錄描述了它能夠自動修復的內容。
print
語句#
在Python 2里,print
是一個語句。無論你想輸出什么,只要將它們放在print
關鍵字后邊就可以。在Python 3里,print()
是一個函數。就像其他的函數一樣,print()
需要你將想要輸出的東西作為參數傳給它。
Notes | Python 2 | Python 3 |
---|---|---|
① | print |
print() |
② | print 1 |
print(1) |
③ | print 1,2 |
print(1,2) |
④ | print 1,2, |
print(1,2, end=' ') |
⑤ | print >>sys.stderr,1, 2, 3 |
print(1,2, 3, file=sys.stderr) |
- 為輸出一個空白行,需要調用不帶參數的
print()
。 - 為輸出一個單獨的值,需要將這這個值作為
print()
的一個參數就可以了。 - 為輸出使用一個空格分隔的兩個值,用兩個參數調用
print()
即可。 - 這個例子有一些技巧。在Python 2里,如果你使用一個逗號(,)作為
print
語句的結尾,它將會用空格分隔輸出的結果,然后在輸出一個尾隨的空格(trailing space),而不輸出回車(carriage return)。在Python 3里,通過把end=' '
作為一個關鍵字參數傳給print()
可以實現同樣的效果。參數end
的默認值為'\n'
,所以通過重新指定end
參數的值,可以取消在末尾輸出回車符。 - 在Python 2里,你可以通過使用
>>pipe_name
語法,把輸出重定向到一個管道,比如sys.stderr
。在Python 3里,你可以通過將管道作為關鍵字參數file
的值傳遞給print()
來完成同樣的功能。參數file
的默認值為std.stdout
,所以重新指定它的值將會使print()
輸出到一個另外一個管道。
Unicode字符串#
Python 2有兩種字符串類型:Unicode字符串和非Unicode字符串。Python 3只有一種類型:Unicode字符串(Unicode strings)。
Notes | Python 2 | Python 3 |
---|---|---|
① | u'PapayaWhip' |
'PapayaWhip' |
② | ur'PapayaWhip\foo' |
r'PapayaWhip\foo' |
- Python 2里的Unicode字符串在Python 3里即普通字符串,因為在Python 3里字符串總是Unicode形式的。
- Unicode原始字符串(raw string)(使用這種字符串,Python不會自動轉義反斜線"\")也被替換為普通的字符串,因為在Python 3里,所有原始字符串都是以Unicode編碼的。
全局函數unicode()
#
Python 2有兩個全局函數可以把對象強制轉換成字符串:unicode()
把對象轉換成Unicode字符串,還有str()
把對象轉換為非Unicode字符串。Python 3只有一種字符串類型,Unicode字符串,所以str()
函數即可完成所有的功能。(unicode()
函數在Python 3里不再存在了。)
Notes | Python 2 | Python 3 |
---|---|---|
unicode(anything) |
str(anything) |
long
長整型#
Python 2有為非浮點數准備的int
和long
類型。int
類型的最大值不能超過sys.maxint
,而且這個最大值是平台相關的。可以通過在數字的末尾附上一個L
來定義長整型,顯然,它比int
類型表示的數字范圍更大。在Python 3里,只有一種整數類型int
,大多數情況下,它很像Python 2里的長整型。由於已經不存在兩種類型的整數,所以就沒有必要使用特殊的語法去區別他們。
Notes | Python 2 | Python 3 |
---|---|---|
① | x =1000000000000L |
x =1000000000000 |
② | x =0xFFFFFFFFFFFFL |
x =0xFFFFFFFFFFFF |
③ | long(x) |
int(x) |
④ | type(x)is long |
type(x)is int |
⑤ | isinstance(x,long) |
isinstance(x,int) |
- 在Python 2里的十進制長整型在Python 3里被替換為十進制的普通整數。
- 在Python 2里的十六進制長整型在Python 3里被替換為十六進制的普通整數。
- 在Python 3里,由於長整型已經不存在了,自然原來的
long()
函數也沒有了。為了強制轉換一個變量到整型,可以使用int()
函數。 - 檢查一個變量是否是整型,獲得它的數據類型,並與一個
int
類型(不是long
)的作比較。 - 你也可以使用
isinstance()
函數來檢查數據類型;再強調一次,使用int
,而不是long
,來檢查整數類型。
<> 比較運算符#
Python 2支持<>
作為!=
的同義詞。Python 3只支持!=
,不再支持<>了。
Notes | Python 2 | Python 3 |
---|---|---|
① | if x <> y: |
if x != y: |
② | if x <> y<> z: |
if x != y!= z: |
- 簡單地比較。
- 相對復雜的三個值之間的比較。
字典類方法has_key()
#
在Python 2里,字典對象的has_key()
方法用來測試字典是否包含特定的鍵(key)。Python 3不再支持這個方法了。你需要使用in
運算符。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_dictionary.has_key('PapayaWhip') |
'PapayaWhip' in a_dictionary |
② | a_dictionary.has_key(x)or a_dictionary.has_key(y) |
x in a_dictionaryor y in a_dictionary |
③ | a_dictionary.has_key(xor y) |
(x or y)in a_dictionary |
④ | a_dictionary.has_key(x+ y) |
(x + y)in a_dictionary |
⑤ | x + a_dictionary.has_key(y) |
x +(y in a_dictionary) |
- 最簡單的形式。
- 運算符
or
的優先級高於運算符in
,所以這里不需要添加括號。 - 另一方面,出於同樣的原因 —
or
的優先級大於in
,這里需要添加括號。(注意:這里的代碼與前面那行完全不同。Python會先解釋x or y
,得到結果x(如果x在布爾上下文里的值是真)或者y。然后Python檢查這個結果是不是a_dictionary的一個鍵。) - 運算符
in
的優先級大於運算符+
,所以代碼里的這種形式從技術上說不需要括號,但是2to3
還是添加了。 - 這種形式一定需要括號,因為
in
的優先級大於+
。
返回列表的字典類方法#
在Python 2里,許多字典類方法的返回值是列表。其中最常用方法的有keys
,items
和values
。在Python 3里,所有以上方法的返回值改為動態視圖(dynamic view)。在一些上下文環境里,這種改變並不會產生影響。如果這些方法的返回值被立即傳遞給另外一個函數,並且那個函數會遍歷整個序列,那么以上方法的返回值是列表或者視圖並不會產生什么不同。在另外一些情況下,Python 3的這些改變干系重大。如果你期待一個能被獨立尋址元素的列表,那么Python 3的這些改變將會使你的代碼卡住(choke),因為視圖(view)不支持索引(indexing)。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_dictionary.keys() |
list(a_dictionary.keys()) |
② | a_dictionary.items() |
list(a_dictionary.items()) |
③ | a_dictionary.iterkeys() |
iter(a_dictionary.keys()) |
④ | [i for iin a_dictionary.iterkeys()] |
[i for iin a_dictionary.keys()] |
⑤ | min(a_dictionary.keys()) |
no change |
- 使用
list()
函數將keys()
的返回值轉換為一個靜態列表,出於安全方面的考量,2to3
可能會報錯。這樣的代碼是有效的,但是對於使用視圖來說,它的效率低一些。你應該檢查轉換后的代碼,看看是否一定需要列表,也許視圖也能完成同樣的工作。 - 這是另外一種視圖(關於
items()
方法的)到列表的轉換。2to3
對values()
方法返回值的轉換也是一樣的。 - Python 3里不再支持
iterkeys()
了。如果必要,使用iter()
將keys()
的返回值轉換成為一個迭代器。 2to3
能夠識別出iterkeys()
方法在列表解析里被使用,然后將它轉換為Python 3里的keys()
方法(不需要使用額外的iter()
去包裝其返回值)。這樣是可行的,因為視圖是可迭代的。2to3
也能識別出keys()
方法的返回值被立即傳給另外一個會遍歷整個序列的函數,所以也就沒有必要先把keys()
的返回值轉換到一個列表。相反的,min()
函數會很樂意遍歷視圖。這個過程對min()
,max()
,sum()
,list()
,tuple()
,set()
,sorted()
,any()
和all()
同樣有效。
被重命名或者重新組織的模塊#
從Python 2到Python 3,標准庫里的一些模塊已經被重命名了。還有一些相互關聯的模塊也被組合或者重新組織,以使得這種關聯更有邏輯性。
http
#
在Python 3里,幾個相關的HTTP模塊被組合成一個單獨的包,即http
。
Notes | Python 2 | Python 3 |
---|---|---|
① | import httplib |
import http.client |
② | import Cookie |
import http.cookies |
③ | import cookielib |
import http.cookiejar |
④ | |
import http.server |
http.client
模塊實現了一個底層的庫,可以用來請求HTTP資源,解析HTTP響應。http.cookies
模塊提供一個蟒樣的(Pythonic)接口來獲取通過HTTP頭部(HTTP header)Set-Cookie發送的cookies- 常用的流行的瀏覽器會把cookies以文件形式存放在磁盤上,
http.cookiejar
模塊可以操作這些文件。 http.server
模塊實現了一個基本的HTTP服務器
urllib
#
Python 2有一些用來分析,編碼和獲取URL的模塊,但是這些模塊就像老鼠窩一樣相互重疊。在Python 3里,這些模塊被重構、組合成了一個單獨的包,即urllib
。
Notes | Python 2 | Python 3 |
---|---|---|
① | import urllib |
import urllib.request, urllib.parse, urllib.error |
② | import urllib2 |
import urllib.request, urllib.error |
③ | import urlparse |
import urllib.parse |
④ | import robotparser |
import urllib.robotparser |
⑤ | |
|
⑥ | |
|
- 以前,Python 2里的
urllib
模塊有各種各樣的函數,包括用來獲取數據的urlopen()
,還有用來將URL分割成其組成部分的splittype()
,splithost()
和splituser()
函數。在新的urllib
包里,這些函數被組織得更有邏輯性。2to3將會修改這些函數的調用以適應新的命名方案。 - 在Python 3里,以前的
urllib2
模塊被並入了urllib
包。同時,以urllib2
里各種你最喜愛的東西將會一個不缺地出現在Python 3的urllib
模塊里,比如build_opener()
方法,Request
對象,HTTPBasicAuthHandler
和friends。 - Python 3里的
urllib.parse
模塊包含了原來Python 2里urlparse
模塊所有的解析函數。 urllib.robotparse
模塊解析robots.txt
文件。- 處理HTTP重定向和其他狀態碼的
FancyURLopener
類在Python 3里的urllib.request
模塊里依然有效。urlencode()
函數已經被轉移到了urllib.parse
里。 Request
對象在urllib.request
里依然有效,但是像HTTPError
這樣的常量已經被轉移到了urllib.error
里。
我是否有提到2to3
也會重寫你的函數調用?比如,如果你的Python 2代碼里導入了urllib
模塊,調用了urllib.urlopen()
函數獲取數據,2to3
會同時修改import
語句和函數調用。
Notes | Python 2 | Python 3 |
---|---|---|
|
|
dbm
#
所有的DBM克隆(DBM clone)現在在單獨的一個包里,即dbm
。如果你需要其中某個特定的變體,比如GNUDBM,你可以導入dbm
包中合適的模塊。
Notes | Python 2 | Python 3 |
---|---|---|
import dbm |
import dbm.ndbm |
|
import gdbm |
import dbm.gnu |
|
import dbhash |
import dbm.bsd |
|
import dumbdbm |
import dbm.dumb |
|
|
import dbm |
xmlrpc
#
XML-RPC是一個通過HTTP協議執行遠程RPC調用的輕重級方法。一些XML-RPC客戶端和XML-RPC服務端的實現庫現在被組合到了獨立的包,即xmlrpc
。
Notes | Python 2 | Python 3 |
---|---|---|
import xmlrpclib |
import xmlrpc.client |
|
|
import xmlrpc.server |
其他模塊#
Notes | Python 2 | Python 3 |
---|---|---|
① | |
import io |
② | |
import pickle |
③ | import __builtin__ |
import builtins |
④ | import copy_reg |
import copyreg |
⑤ | import Queue |
import queue |
⑥ | import SocketServer |
import socketserver |
⑦ | import ConfigParser |
import configparser |
⑧ | import repr |
import reprlib |
⑨ | import commands |
import subprocess |
- 在Python 2里,你通常會這樣做,首先嘗試把
cStringIO
導入作為StringIO
的替代,如果失敗了,再導入StringIO
。不要在Python 3里這樣做;io
模塊會幫你處理好這件事情。它會找出可用的最快實現方法,然后自動使用它。 - 在Python 2里,導入最快的
pickle
實現也是一個與上邊相似的能用方法。在Python 3里,pickle
模塊會自動為你處理,所以不要再這樣做。 builtins
模塊包含了在整個Python語言里都會使用的全局函數,類和常量。重新定義builtins
模塊里的某個函數意味着在每處都重定義了這個全局函數。這聽起來很強大,但是同時也是很可怕的。copyreg
模塊為用C語言定義的用戶自定義類型添加了pickle
模塊的支持。queue
模塊實現一個生產者消費者隊列(multi-producer, multi-consumer queue)。socketserver
模塊為實現各種socket server提供了通用基礎類。configparser
模塊用來解析INI-style配置文件。reprlib
模塊重新實現了內置函數repr()
,並添加了對字符串表示被截斷前長度的控制。subprocess
模塊允許你創建子進程,連接到他們的管道,然后獲取他們的返回值。
包內的相對導入#
包是由一組相關聯的模塊共同組成的單個實體。在Python 2的時候,為了實現同一個包內模塊的相互引用,你會使用import foo
或者from foo import Bar
。Python 2解釋器會先在當前目錄里搜索foo.py
,然后再去Python搜索路徑(sys.path
)里搜索。在Python 3里這個過程有一點不同。Python 3不會首先在當前路徑搜索,它會直接在Python的搜索路徑里尋找。如果你想要包里的一個模塊導入包里的另外一個模塊,你需要顯式地提供兩個模塊的相對路徑。
假設你有如下包,多個文件在同一個目錄下:
chardet/ | +--__init__.py | +--constants.py | +--mbcharsetprober.py | +--universaldetector.py
現在假設universaldetector.py
需要整個導入constants.py
,另外還需要導入mbcharsetprober.py
的一個類。你會怎樣做?
Notes | Python 2 | Python 3 |
---|---|---|
① | import constants |
from .import constants |
② | from mbcharsetproberimport MultiByteCharSetProber |
from .mbcharsetproberimport MultiByteCharsetProber |
- 當你需要從包的其他地方導入整個模塊,使用新的
from . import
語法。這里的句號(.)即表示當前文件(universaldetector.py
)和你想要導入文件(constants.py
)之間的相對路徑。在這個樣例中,這兩個文件在同一個目錄里,所以使用了單個句號。你也可以從父目錄(from .. import anothermodule
)或者子目錄里導入。 - 為了將一個特定的類或者函數從其他模塊里直接導入到你的模塊的名字空間里,在需要導入的模塊名前加上相對路徑,並且去掉最后一個斜線(slash)。在這個例子中,
mbcharsetprober.py
與universaldetector.py
在同一個目錄里,所以相對路徑名就是一個句號。你也可以從父目錄(from .. import anothermodule)或者子目錄里導入。
迭代器方法next()
#
在Python 2里,迭代器有一個next()
方法,用來返回序列里的下一項。在Python 3里這同樣成立,但是現在有了一個新的全局的函數next()
,它使用一個迭代器作為參數。
Notes | Python 2 | Python 3 |
---|---|---|
① | anIterator.next() |
next(anIterator) |
② | a_function_that_returns_an_iterator().next() |
next(a_function_that_returns_an_iterator()) |
③ | |
|
④ | |
no change |
⑤ | |
|
- 最簡單的例子,你不再調用一個迭代器的
next()
方法,現在你將迭代器自身作為參數傳遞給全局函數next()
。 - 假如你有一個返回值是迭代器的函數,調用這個函數然后把結果作為參數傳遞給
next()
函數。(2to3
腳本足夠智能以正確執行這種轉換。) - 假如你想定義你自己的類,然后把它用作一個迭代器,在Python 3里,你可以通過定義特殊方法
__next__()
來實現。 - 如果你定義的類里剛好有一個
next()
,它使用一個或者多個參數,2to3
執行的時候不會動它。這個類不能被當作迭代器使用,因為它的next()
方法帶有參數。 - 這一個有些復雜。如果你恰好有一個叫做next的本地變量,在Python 3里它的優先級會高於全局函數
next()
。在這種情況下,你需要調用迭代器的特別方法__next__()
來獲取序列里的下一個元素。(或者,你也可以重構代碼以使這個本地變量的名字不叫next,但是2to3不會為你做這件事。)
全局函數filter()
#
在Python 2里,filter()
方法返回一個列表,這個列表是通過一個返回值為True
或者False
的函數來檢測序列里的每一項得到的。在Python 3里,filter()
函數返回一個迭代器,不再是列表。
Notes | Python 2 | Python 3 |
---|---|---|
① | filter(a_function, a_sequence) |
list(filter(a_function, a_sequence)) |
② | list(filter(a_function, a_sequence)) |
no change |
③ | filter(None, a_sequence) |
[i for iin a_sequence if i] |
④ | for i in filter(None, a_sequence): |
no change |
⑤ | [i for iin filter(a_function, a_sequence)] |
no change |
- 最簡單的情況下,
2to3
會用一個list()
函數來包裝filter()
,list()
函數會遍歷它的參數然后返回一個列表。 - 然而,如果
filter()
調用已經被list()
包裹,2to3
不會再做處理,因為這種情況下filter()
的返回值是否是一個迭代器是無關緊要的。 - 為了處理
filter(None, ...)
這種特殊的語法,2to3
會將這種調用從語法上等價地轉換為列表解析。 - 由於
for
循環會遍歷整個序列,所以沒有必要再做修改。 - 與上面相同,不需要做修改,因為列表解析會遍歷整個序列,即使
filter()
返回一個迭代器,它仍能像以前的filter()
返回列表那樣正常工作。
全局函數map()
#
跟filter()
作的改變一樣,map()
函數現在返回一個迭代器。(在Python 2里,它返回一個列表。)
Notes | Python 2 | Python 3 |
---|---|---|
① | map(a_function,'PapayaWhip') |
list(map(a_function,'PapayaWhip')) |
② | map(None,'PapayaWhip') |
list('PapayaWhip') |
③ | map(lambda x: x+1, range(42)) |
[x+1for x in range(42)] |
④ | for i in map(a_function, a_sequence): |
no change |
⑤ | [i for iin map(a_function, a_sequence)] |
no change |
- 類似對
filter()
的處理,在最簡單的情況下,2to3
會用一個list()
函數來包裝map()
調用。 - 對於特殊的
map(None, ...)
語法,跟filter(None, ...)
類似,2to3
會將其轉換成一個使用list()
的等價調用 - 如果
map()
的第一個參數是一個lambda函數,2to3
會將其等價地轉換成列表解析。 - 對於會遍歷整個序列的
for
循環,不需要做改變。 - 再一次地,這里不需要做修改,因為列表解析會遍歷整個序列,即使
map()
的返回值是迭代器而不是列表它也能正常工作。
全局函數reduce()
#
在Python 3里,reduce()
函數已經被從全局名字空間里移除了,它現在被放置在fucntools
模塊里。
Notes | Python 2 | Python 3 |
---|---|---|
reduce(a, b, c) |
|
全局函數apply()
#
Python 2有一個叫做apply()
的全局函數,它使用一個函數f和一個列表[a, b, c]
作為參數,返回值是f(a, b, c)
。你也可以通過直接調用這個函數,在列表前添加一個星號(*)作為參數傳遞給它來完成同樣的事情。在Python 3里,apply()
函數不再存在了;必須使用星號標記法。
Notes | Python 2 | Python 3 |
---|---|---|
① | apply(a_function, a_list_of_args) |
a_function(*a_list_of_args) |
② | apply(a_function, a_list_of_args, a_dictionary_of_named_args) |
a_function(*a_list_of_args,**a_dictionary_of_named_args) |
③ | apply(a_function, a_list_of_args+ z) |
a_function(*a_list_of_args+ z) |
④ | apply(aModule.a_function, a_list_of_args) |
aModule.a_function(*a_list_of_args) |
- 最簡單的形式,可以通過在參數列表(就像
[a, b, c]
一樣)前添加一個星號來調用函數。這跟Python 2里的apply()
函數是等價的。 - 在Python 2里,
apply()
函數實際上可以帶3個參數:一個函數,一個參數列表,一個字典命名參數(dictionary of named arguments)。在Python 3里,你可以通過在參數列表前添加一個星號(*
),在字典命名參數前添加兩個星號(**
)來達到同樣的效果。 - 運算符
+
在這里用作連接列表的功能,它的優先級高於運算符*
,所以沒有必要在a_list_of_args + z
周圍添加額外的括號。 2to3
腳本足夠智能來轉換復雜的apply()
調用,包括調用導入模塊里的函數。
全局函數intern()
#
在Python 2里,你可以用intern()
函數作用在一個字符串上來限定(intern)它以達到性能優化。在Python 3里,intern()
函數被轉移到sys
模塊里了。
Notes | Python 2 | Python 3 |
---|---|---|
intern(aString) |
sys.intern(aString) |
exec
語句#
就像print
語句在Python 3里變成了一個函數一樣,exec
語句也是這樣的。exec()
函數使用一個包含任意Python代碼的字符串作為參數,然后就像執行語句或者表達式一樣執行它。exec()
跟eval()
是相似的,但是exec()
更加強大並更具有技巧性。eval()
函數只能執行單獨一條表達式,但是
能夠執行多條語句,導入(import),函數聲明 — 實際上整個Python程序的字符串表示也可以。exec
()
Notes | Python 2 | Python 3 |
---|---|---|
① | exec codeString |
exec(codeString) |
② | exec codeString in a_global_namespace |
exec(codeString, a_global_namespace) |
③ | exec codeString in a_global_namespace, a_local_namespace |
exec(codeString, a_global_namespace, a_local_namespace) |
- 在最簡單的形式下,因為
exec()
現在是一個函數,而不是語句,2to3
會把這個字符串形式的代碼用括號圍起來。 - Python 2里的
exec
語句可以指定名字空間,代碼將在這個由全局對象組成的私有空間里執行。Python 3也有這樣的功能;你只需要把這個名字空間作為第二個參數傳遞給exec()
函數。 - 更加神奇的是,Python 2里的
exec
語句還可以指定一個本地名字空間(比如一個函數里聲明的變量)。在Python 3里,exec()
函數也有這樣的功能。
execfile
語句#
就像以前的exec
語句,Python 2里的execfile
語句也可以像執行Python代碼那樣使用字符串。不同的是exec
使用字符串,而execfile
則使用文件。在Python 3里,execfile
語句已經被去掉了。如果你真的想要執行一個文件里的Python代碼(但是你不想導入它),你可以通過打開這個文件,讀取它的內容,然后調用compile()
全局函數強制Python解釋器編譯代碼,然后調用新的exec()
函數。
Notes | Python 2 | Python 3 |
---|---|---|
execfile('a_filename') |
exec(compile(open('a_filename').read(),'a_filename','exec')) |
repr
(反引號)#
在Python 2里,為了得到一個任意對象的字符串表示,有一種把對象包裝在反引號里(比如`x`
)的特殊語法。在Python 3里,這種能力仍然存在,但是你不能再使用反引號獲得這種字符串表示了。你需要使用全局函數repr()
。
Notes | Python 2 | Python 3 |
---|---|---|
① | `x` |
repr(x) |
② | `'PapayaWhip' + `2`` |
repr('PapayaWhip'+ repr(2)) |
- 記住,x可以是任何東西 — 一個類,函數,模塊,基本數據類型,等等。
repr()
函數可以使用任何類型的參數。 - 在Python 2里,反引號可以嵌套,導致了這種令人費解的(但是有效的)表達式。
2to3
足夠智能以將這種嵌套調用轉換到repr()
函數。
try...except
語句#
從Python 2到Python 3,捕獲異常的語法有些許變化。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
|
② | |
|
③ | |
no change |
④ | |
no change |
- 相對於Python 2里在異常類型后添加逗號,Python 3使用了一個新的關鍵字,
as
。 - 關鍵字
as
也可以用在一次捕獲多種類型異常的情況下。 - 如果你捕獲到一個異常,但是並不在意訪問異常對象本身,Python 2和Python 3的語法是一樣的。
- 類似地,如果你使用一個保險方法(fallback)來捕獲所有異常,Python 2和Python 3的語法是一樣的。
☞在導入模塊(或者其他大多數情況)的時候,你絕對不應該使用這種方法(指以上的fallback)。不然的話,程序可能會捕獲到像
KeyboardInterrupt
(如果用戶按Ctrl-C來中斷程序)這樣的異常,從而使調試變得更加困難。
raise
語句#
Python 3里,拋出自定義異常的語法有細微的變化。
Notes | Python 2 | Python 3 |
---|---|---|
① | raise MyException |
unchanged |
② | raise MyException,'error message' |
raise MyException('error message') |
③ | raise MyException,'error message', a_traceback |
raise MyException('error message').with_traceback(a_traceback) |
④ | raise 'error message' |
unsupported |
- 拋出不帶用戶自定義錯誤信息的異常,這種最簡單的形式下,語法沒有改變。
- 當你想要拋出一個帶用戶自定義錯誤信息的異常時,改變就顯而易見了。Python 2用一個逗號來分隔異常類和錯誤信息;Python 3把錯誤信息作為參數傳遞給異常類。
- Python 2支持一種更加復雜的語法來拋出一個帶用戶自定義回溯(stack trace,堆棧追蹤)的異常。在Python 3里你也可以這樣做,但是語法完全不同。
- 在Python 2里,你可以拋出一個不帶異常類的異常,僅僅只有一個異常信息。在Python 3里,這種形式不再被支持。
2to3
將會警告你它不能自動修復這種語法。
生成器的throw
方法#
在Python 2里,生成器有一個throw()
方法。調用a_generator.throw()
會在生成器被暫停的時候拋出一個異常,然后返回由生成器函數獲取的下一個值。在Python 3里,這種功能仍然可用,但是語法上有一點不同。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_generator.throw(MyException) |
no change |
② | a_generator.throw(MyException,'error message') |
a_generator.throw(MyException('error message')) |
③ | a_generator.throw('error message') |
unsupported |
- 最簡單的形式下,生成器拋出不帶用戶自定義錯誤信息的異常。這種情況下,從Python 2到Python 3語法上沒有變化 。
- 如果生成器拋出一個帶用戶自定義錯誤信息的異常,你需要將這個錯誤信息字符串(error string)傳遞給異常類來以實例化它。
- Python 2還支持拋出只有異常信息的異常。Python 3不支持這種語法,並且
2to3
會顯示一個警告信息,告訴你需要手動地來修復這處代碼。
全局函數xrange()
#
在Python 2里,有兩種方法來獲得一定范圍內的數字:range()
,它返回一個列表,還有range()
,它返回一個迭代器。在Python 3里,range()
返回迭代器,xrange()
不再存在了。
Notes | Python 2 | Python 3 |
---|---|---|
① | xrange(10) |
range(10) |
② | a_list = range(10) |
a_list = list(range(10)) |
③ | [i for iin xrange(10)] |
[i for iin range(10)] |
④ | for i in range(10): |
no change |
⑤ | sum(range(10)) |
no change |
- 在最簡單的情況下,
2to3
會簡單地把xrange()
轉換為range()
。 - 如果你的Python 2代碼使用
range()
,2to3
不知道你是否需要一個列表,或者是否一個迭代器也行。出於謹慎,2to3
可能會報錯,然后使用list()
把range()
的返回值強制轉換為列表類型。 - 如果在列表解析里有
xrange()
函數,就沒有必要將其返回值轉換為一個列表,因為列表解析對迭代器同樣有效。 - 類似的,
for
循環也能作用於迭代器,所以這里也沒有改變任何東西。 - 函數
sum()
能作用於迭代器,所以2to3
也沒有在這里做出修改。就像返回值為視圖(view)而不再是列表的字典類方法一樣,這同樣適用於min()
,max()
,sum()
,list(),tuple()
,set()
,sorted()
,any()
,all()
。
全局函數raw_input()
和input()
#
Python 2有兩個全局函數,用來在命令行請求用戶輸入。第一個叫做input()
,它等待用戶輸入一個Python表達式(然后返回結果)。第二個叫做raw_input()
,用戶輸入什么它就返回什么。這讓初學者非常困惑,並且這被廣泛地看作是Python語言的一個“肉贅”(wart)。Python 3通過重命名raw_input()
為input()
,從而切掉了這個肉贅,所以現在的input()
就像每個人最初期待的那樣工作。
Notes | Python 2 | Python 3 |
---|---|---|
① | raw_input() |
input() |
② | raw_input('prompt') |
input('prompt') |
③ | input() |
eval(input()) |
- 最簡單的形式,
raw_input()
被替換成input()
。 - 在Python 2里,
raw_input()
函數可以指定一個提示符作為參數。Python 3里保留了這個功能。 - 如果你真的想要請求用戶輸入一個Python表達式,計算結果,可以通過調用
input()
函數然后把返回值傳遞給eval()
。
函數屬性func_*
#
在Python 2里,函數的里的代碼可以訪問到函數本身的特殊屬性。在Python 3里,為了一致性,這些特殊屬性被重新命名了。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_function.func_name |
a_function.__name__ |
② | a_function.func_doc |
a_function.__doc__ |
③ | a_function.func_defaults |
a_function.__defaults__ |
④ | a_function.func_dict |
a_function.__dict__ |
⑤ | a_function.func_closure |
a_function.__closure__ |
⑥ | a_function.func_globals |
a_function.__globals__ |
⑦ | a_function.func_code |
a_function.__code__ |
__name__
屬性(原func_name
)包含了函數的名字。__doc__
屬性(原funcdoc
)包含了你在函數源代碼里定義的文檔字符串(docstring)__defaults__
屬性(原func_defaults
)是一個保存參數默認值的元組。__dict__
屬性(原func_dict
)是一個支持任意函數屬性的名字空間。__closure__
屬性(原func_closure
)是一個由cell對象組成的元組,它包含了函數對自由變量(free variable)的綁定。__globals__
屬性(原func_globals
)是一個對模塊全局名字空間的引用,函數本身在這個名字空間里被定義。__code__
屬性(原func_code
)是一個代碼對象,表示編譯后的函數體。
I/O方法xreadlines()
#
在Python 2里,文件對象有一個xreadlines()
方法,它返回一個迭代器,一次讀取文件的一行。這在for
循環中尤其有用。事實上,后來的Python 2版本給文件對象本身添加了這樣的功能。
在Python 3里,xreadlines()
方法不再可用了。2to3
可以解決簡單的情況,但是一些邊緣案例則需要人工介入。
Notes | Python 2 | Python 3 |
---|---|---|
① | for line in a_file.xreadlines(): |
for line in a_file: |
② | for line in a_file.xreadlines(5): |
no change (broken) |
- 如果你以前調用沒有參數的
xreadlines()
,2to3
會把它轉換成文件對象本身。在Python 3里,這種轉換后的代碼可以完成前同樣的工作:一次讀取文件的一行,然后執行for
循環的循環體。 - 如果你以前使用一個參數(每次讀取的行數)調用
xreadlines()
,2to3
不能為你完成從Python 2到Python 3的轉換,你的代碼會以這樣的方式失敗:AttributeError: '_io.TextIOWrapper' object has no attribute 'xreadlines'
。你可以手工的把xreadlines()
改成readlines()
以使代碼能在Python 3下工作。(readline()方法在Python 3里返回迭代器,所以它跟Python 2里的xreadlines()
效率是不相上下的。)
☃
使用元組而非多個參數的lambda
函數#
在Python 2里,你可以定義匿名lambda
函數(anonymous lambda
function),通過指定作為參數的元組的元素個數,使這個函數實際上能夠接收多個參數。事實上,Python 2的解釋器把這個元組“解開”(unpack)成命名參數(named arguments),然后你可以在lambda
函數里引用它們(通過名字)。在Python 3里,你仍然可以傳遞一個元組作為lambda
函數的參數,但是Python解釋器不會把它解析成命名參數。你需要通過位置索引(positional index)來引用每個參數。
Notes | Python 2 | Python 3 |
---|---|---|
① | lambda (x,): x+ f(x) |
lambda x1: x1[0]+ f(x1[0]) |
② | lambda (x, y): x+ f(y) |
lambda x_y: x_y[0]+ f(x_y[1]) |
③ | lambda (x,(y, z)): x+ y + z |
lambda x_y_z: x_y_z[0]+ x_y_z[1][0]+ x_y_z[1][1] |
④ | lambda x, y, z: x+ y + z |
unchanged |
- 如果你已經定義了一個
lambda
函數,它使用包含一個元素的元組作為參數,在Python 3里,它會被轉換成一個包含到x1[0]的引用的lambda
函數。x1是2to3
腳本基於原來元組里的命名參數自動生成的。 - 使用含有兩個元素的元組(x, y)作為參數的
lambda
函數被轉換為x_y,它有兩個位置參數,即x_y[0]和x_y[1]。 2to3
腳本甚至可以處理使用嵌套命名參數的元組作為參數的lambda
函數。產生的結果代碼有點難以閱讀,但是它在Python 3下跟原來的代碼在Python 2下的效果是一樣的。- 你可以定義使用多個參數的
lambda
函數。如果沒有括號包圍在參數周圍,Python 2會把它當作一個包含多個參數的lambda
函數;在這個lambda
函數體里,你通過名字引用這些參數,就像在其他類型的函數里所做的一樣。這種語法在Python 3里仍然有效。
特殊的方法屬性#
在Python 2里,類方法可以訪問到定義他們的類對象(class object),也能訪問方法對象(method object)本身。im_self
是類的實例對象;im_func
是函數對象,im_class
是類本身。在Python 3里,這些屬性被重新命名,以遵循其他屬性的命名約定。
Notes | Python 2 | Python 3 |
---|---|---|
aClassInstance.aClassMethod.im_func |
aClassInstance.aClassMethod.__func__ |
|
aClassInstance.aClassMethod.im_self |
aClassInstance.aClassMethod.__self__ |
|
aClassInstance.aClassMethod.im_class |
aClassInstance.aClassMethod.__self__.__class__ |
__nonzero__
特殊方法#
在Python 2里,你可以創建自己的類,並使他們能夠在布爾上下文(boolean context)中使用。舉例來說,你可以實例化這個類,並把這個實例對象用在一個if
語句中。為了實現這個目的,你定義一個特別的__nonzero__()
方法,它的返回值為True
或者False
,當實例對象處在布爾上下文中的時候這個方法就會被調用 。在Python 3里,你仍然可以完成同樣的功能,但是這個特殊方法的名字變成了__bool__()
。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
|
② | |
no change |
- 當在布爾上下文使用一個類對象時,Python 3會調用
__bool__()
,而非__nonzero__()
。 - 然而,如果你有定義了一個使用兩個參數的
__nonzero__()
方法,2to3
腳本會假設你定義的這個方法有其他用處,因此不會對代碼做修改。
八進制類型#
在Python 2和Python 3之間,定義八進制(octal)數的語法有輕微的改變。
Notes | Python 2 | Python 3 |
---|---|---|
x =0755 |
x =0o755 |
sys.maxint
#
由於長整型和整型被整合在一起了,sys.maxint
常量不再精確。但是因為這個值對於檢測特定平台的能力還是有用處的,所以它被Python 3保留,並且重命名為sys.maxsize
。
Notes | Python 2 | Python 3 |
---|---|---|
① | from sys importmaxint |
from sys importmaxsize |
② | a_function(sys.maxint) |
a_function(sys.maxsize) |
maxint
變成了maxsize
。- 所有的
sys.maxint
都變成了sys.maxsize
。
全局函數callable()
#
在Python 2里,你可以使用全局函數callable()
來檢查一個對象是否可調用(callable,比如函數)。在Python 3里,這個全局函數被取消了。為了檢查一個對象是否可調用,可以檢查特殊方法__call__()
的存在性。
Notes | Python 2 | Python 3 |
---|---|---|
callable(anything) |
hasattr(anything,'__call__') |
全局函數zip()
#
在Python 2里,全局函數zip()
可以使用任意多個序列作為參數,它返回一個由元組構成的列表。第一個元組包含了每個序列的第一個元素;第二個元組包含了每個序列的第二個元素;依次遞推下去。在Python 3里,zip()
返回一個迭代器,而非列表。
Notes | Python 2 | Python 3 |
---|---|---|
① | zip(a, b, c) |
list(zip(a, b, c)) |
② | d.join(zip(a, b, c)) |
no change |
- 最簡單的形式,你可以通過調用
list()
函數包裝zip()
的返回值來恢復zip()
函數以前的功能,list()
函數會遍歷這個zip()
函數返回的迭代器,然后返回結果的列表表示。 - 在已經會遍歷序列所有元素的上下文環境里(比如這里對
join()
方法的調用),zip()
返回的迭代器能夠正常工作。2to3
腳本會檢測到這些情況,不會對你的代碼作出改變。
StandardError
異常#
在Python 2里,StandardError
是除了StopIteration
,GeneratorExit
,KeyboardInterrupt
,SystemExit
之外所有其他內置異常的基類。在Python 3里,StandardError
已經被取消了;使用Exception
替代。
Notes | Python 2 | Python 3 |
---|---|---|
x =StandardError() |
x =Exception() |
|
x =StandardError(a, b, c) |
x =Exception(a, b, c) |
types
模塊中的常量#
types
模塊里各種各樣的常量能幫助你決定一個對象的類型。在Python 2里,它包含了代表所有基本數據類型的常量,如dict
和int
。在Python 3里,這些常量被已經取消了。只需要使用基礎類型的名字來替代。
Notes | Python 2 | Python 3 |
---|---|---|
types.UnicodeType |
str |
|
types.StringType |
bytes |
|
types.DictType |
dict |
|
types.IntType |
int |
|
types.LongType |
int |
|
types.ListType |
list |
|
types.NoneType |
type(None) |
|
types.BooleanType |
bool |
|
types.BufferType |
memoryview |
|
types.ClassType |
type |
|
types.ComplexType |
complex |
|
types.EllipsisType |
type(Ellipsis) |
|
types.FloatType |
float |
|
types.ObjectType |
object |
|
types.NotImplementedType |
type(NotImplemented) |
|
types.SliceType |
slice |
|
types.TupleType |
tuple |
|
types.TypeType |
type |
|
types.XRangeType |
range |
☞
types.StringType
被映射為bytes
,而非str
,因為Python 2里的“string”(非Unicode編碼的字符串,即普通字符串)事實上只是一些使用某種字符編碼的字節序列(a sequence of bytes)。
全局函數isinstance()
#
isinstance()
函數檢查一個對象是否是一個特定類(class)或者類型(type)的實例。在Python 2里,你可以傳遞一個由類型(types)構成的元組給isinstance()
,如果該對象是元組里的任意一種類型,函數返回True
。在Python 3里,你依然可以這樣做,但是不推薦使用把一種類型作為參數傳遞兩次。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x,(int,float,int)) |
isinstance(x,(int,float)) |
basestring
數據類型#
Python 2有兩種字符串類型:Unicode編碼的字符串和非Unicode編碼的字符串。但是其實還有另外 一種類型,即basestring
。它是一個抽象數據類型,是str
和unicode
類型的超類(superclass)。它不能被直接調用或者實例化,但是你可以把它作為isinstance()
的參數來檢測一個對象是否是一個Unicode字符串或者非Unicode字符串。在Python 3里,只有一種字符串類型,所以basestring
就沒有必要再存在了。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x, basestring) |
isinstance(x, str) |
itertools
模塊#
Python 2.3引入了itertools
模塊,它定義了全局函數zip()
,map()
,filter()
的變體(variant),這些變體的返回類型為迭代器,而非列表。在Python 3里,由於這些全局函數的返回類型本來就是迭代器,所以這些itertools
里的這些變體函數就被取消了。(在itertools
模塊里仍然還有許多其他的有用的函數,而不僅僅是以上列出的這些。)
Notes | Python 2 | Python 3 |
---|---|---|
① | itertools.izip(a, b) |
zip(a, b) |
② | itertools.imap(a, b) |
map(a, b) |
③ | itertools.ifilter(a, b) |
filter(a, b) |
④ | from itertools import imap, izip, foo |
from itertools import foo |
- 使用全局的
zip()
函數,而非itertools.izip()
。 - 使用
map()
而非itertools.imap()
。 itertools.ifilter()
變成了filter()
。itertools
模塊在Python 3里仍然存在,它只是不再包含那些已經轉移到全局名字空間的函數。2to3
腳本能夠足夠智能地去移除那些不再有用的導入語句,同時保持其他的導入語句的完整性。
sys.exc_type
, sys.exc_value
, sys.exc_traceback
#
處理異常的時候,在sys
模塊里有三個你可以訪問的變量:sys.exc_type,
sys.exc_value,
sys.exc_traceback
。(實際上這些在Python 1的時代就有。)從Python 1.5開始,由於新出的sys.exc_info
,不再推薦使用這三個變量了,這是一個包含所有以上三個元素的元組。在Python 3里,這三個變量終於不再存在了;這意味着,你必須使用sys.exc_info
。
Notes | Python 2 | Python 3 |
---|---|---|
sys.exc_type |
sys.exc_info()[0] |
|
sys.exc_value |
sys.exc_info()[1] |
|
sys.exc_traceback |
sys.exc_info()[2] |
對元組的列表解析#
在Python 2里,如果你需要編寫一個遍歷元組的列表解析,你不需要在元組值的周圍加上括號。在Python 3里,這些括號是必需的。
Notes | Python 2 | Python 3 |
---|---|---|
[i for iin 1,2] |
[i for iin (1,2)] |
os.getcwdu()
函數#
Python 2有一個叫做os.getcwd()
的函數,它將當前的工作目錄作為一個(非Unicode編碼的)字符串返回。由於現代的文件系統能夠處理能何字符編碼的目錄名,Python 2.3引入了os.getcwdu()
函數。os.getcwdu()
函數把當前工作目錄用Unicode編碼的字符串返回。在Python 3里,由於只有一種字符串類型(Unicode類型的),所以你只需要os.getcwd()
就可以了。
Notes | Python 2 | Python 3 |
---|---|---|
os.getcwdu() |
os.getcwd() |
元類(metaclass)#
在Python 2里,你可以通過在類的聲明中定義metaclass
參數,或者定義一個特殊的類級別的(class-level)__metaclass__
屬性,來創建元類。在Python 3里,__metaclass__
屬性已經被取消了。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
unchanged |
② | |
|
③ | |
|
- 在聲明類的時候聲明
metaclass
參數,這在Python 2和Python 3里都有效,它們是一樣的。 - 在類的定義里聲明
__metaclass__
屬性在Python 2里有效,但是在Python 3里不再有效。 2to3
能夠構建一個有效的類聲明,即使這個類繼承自多個父類。
關於代碼風格#
以下所列的“修補”(fixes)實質上並不算真正的修補。意思就是,他們只是代碼的風格上的事情,而不涉及到代碼的本質。但是Python的開發者們在使得代碼風格盡可能一致方面非常有興趣(have a vested interest)。為此,有一個專門o描述Python代碼風格的官方指導手冊 — 細致到能使人痛苦 — 都是一些你不太可能關心的在各種各樣的細節上的挑剔。鑒於2to3
為轉換代碼提供了一個這么好的條件,腳本的作者們添加了一些可選的特性以使你的代碼更具可讀性。
set()
字面值(literal)(顯式的)#
在Python 2城,定義一個字面值集合(literal set)的唯一方法就是調用set(a_sequence)
。在Python 3里這仍然有效,但是使用新的標注記號(literal notation):大括號({})是一種更清晰的方法。這種方法除了空集以外都有效,因為字典也用大括號標記,所以{}
表示一個空的字典,而不是一個空集。
☞
2to3
腳本默認不會修復set()
字面值。為了開啟這個功能,在命令行調用2to3
的時候指定-f set_literal參數。
Notes | Before | After |
---|---|---|
set([1,2, 3]) |
{1,2, 3} |
|
set((1,2, 3)) |
{1,2, 3} |
|
set([ifor i in a_sequence]) |
{i for iin a_sequence} |
全局函數buffer()
(顯式的)#
用C實現的Python對象可以導出一個“緩沖區接口”(buffer interface),它允許其他的Python代碼直接讀寫一塊內存。(這聽起來很強大,它也同樣可怕。)在Python 3里,buffer()
被重新命名為memoryview()
。(實際的修改更加復雜,但是你幾乎可以忽略掉這些不同之處。)
☞
2to3
腳本默認不會修復buffer()
函數。為了開啟這個功能,在命令行調用2to3
的時候指定-f buffer參數。
Notes | Before | After |
---|---|---|
x =buffer(y) |
x =memoryview(y) |
逗號周圍的空格(顯式的)#
盡管Python對用於縮進和凸出(indenting and outdenting)的空格要求很嚴格,但是對於空格在其他方面的使用Python還是很自由的。在列表,元組,集合和字典里,空格可以出現在逗號的前面或者后面,這不會有什么壞影響。但是,Python代碼風格指導手冊上指出,逗號前不能有空格,逗號后應該包含一個空格。盡管這純粹只是一個美觀上的考量(代碼仍然可以正常工作,在Python 2和Python 3里都可以),但是2to3
腳本可以依據手冊上的標准為你完成這個修復。
☞
2to3
腳本默認不會修復逗號周圍的空格。為了開啟這個功能,在命令行調用2to3
的時候指定-f wscomma參數。
Notes | Before | After |
---|---|---|
a ,b |
a, b |
|
{a :b} |
{a: b} |
慣例(Common idioms)(顯式的)#
在Python社區里建立起來了許多慣例。有一些比如while 1:
loop,它可以追溯到Python 1。(Python直到Python 2.3才有真正意義上的布爾類型,所以開發者以前使用1
和0
替代。)當代的Python程序員應該鍛煉他們的大腦以使用這些慣例的現代版。
☞
2to3
腳本默認不會為這些慣例做修復。為了開啟這個功能,在命令行調用2to3
的時候指定-f idioms參數。
Notes | Before | After |
---|---|---|
|
|
|
type(x)== T |
isinstance(x, T) |
|
type(x)is T |
isinstance(x, T) |
|
|
|
python2 與 python3 語法區別
概述#
幾乎所有的Python 2程序都需要一些修改才能正常地運行在Python 3的環境下。為了簡化這個轉換過程,Python 3自帶了一個叫做2to3
的實用腳本(Utility Script),這個腳本會將你的Python 2程序源文件作為輸入,然后自動將其轉換到Python 3的形式。案例研究:將chardet
移植到Python 3(porting chardet to Python 3)描述了如何運行這個腳本,然后展示了一些它不能自動修復的情況。這篇附錄描述了它能夠自動修復的內容。
print
語句#
在Python 2里,print
是一個語句。無論你想輸出什么,只要將它們放在print
關鍵字后邊就可以。在Python 3里,print()
是一個函數。就像其他的函數一樣,print()
需要你將想要輸出的東西作為參數傳給它。
Notes | Python 2 | Python 3 |
---|---|---|
① | print |
print() |
② | print 1 |
print(1) |
③ | print 1,2 |
print(1,2) |
④ | print 1,2, |
print(1,2, end=' ') |
⑤ | print >>sys.stderr,1, 2, 3 |
print(1,2, 3, file=sys.stderr) |
- 為輸出一個空白行,需要調用不帶參數的
print()
。 - 為輸出一個單獨的值,需要將這這個值作為
print()
的一個參數就可以了。 - 為輸出使用一個空格分隔的兩個值,用兩個參數調用
print()
即可。 - 這個例子有一些技巧。在Python 2里,如果你使用一個逗號(,)作為
print
語句的結尾,它將會用空格分隔輸出的結果,然后在輸出一個尾隨的空格(trailing space),而不輸出回車(carriage return)。在Python 3里,通過把end=' '
作為一個關鍵字參數傳給print()
可以實現同樣的效果。參數end
的默認值為'\n'
,所以通過重新指定end
參數的值,可以取消在末尾輸出回車符。 - 在Python 2里,你可以通過使用
>>pipe_name
語法,把輸出重定向到一個管道,比如sys.stderr
。在Python 3里,你可以通過將管道作為關鍵字參數file
的值傳遞給print()
來完成同樣的功能。參數file
的默認值為std.stdout
,所以重新指定它的值將會使print()
輸出到一個另外一個管道。
Unicode字符串#
Python 2有兩種字符串類型:Unicode字符串和非Unicode字符串。Python 3只有一種類型:Unicode字符串(Unicode strings)。
Notes | Python 2 | Python 3 |
---|---|---|
① | u'PapayaWhip' |
'PapayaWhip' |
② | ur'PapayaWhip\foo' |
r'PapayaWhip\foo' |
- Python 2里的Unicode字符串在Python 3里即普通字符串,因為在Python 3里字符串總是Unicode形式的。
- Unicode原始字符串(raw string)(使用這種字符串,Python不會自動轉義反斜線"\")也被替換為普通的字符串,因為在Python 3里,所有原始字符串都是以Unicode編碼的。
全局函數unicode()
#
Python 2有兩個全局函數可以把對象強制轉換成字符串:unicode()
把對象轉換成Unicode字符串,還有str()
把對象轉換為非Unicode字符串。Python 3只有一種字符串類型,Unicode字符串,所以str()
函數即可完成所有的功能。(unicode()
函數在Python 3里不再存在了。)
Notes | Python 2 | Python 3 |
---|---|---|
unicode(anything) |
str(anything) |
long
長整型#
Python 2有為非浮點數准備的int
和long
類型。int
類型的最大值不能超過sys.maxint
,而且這個最大值是平台相關的。可以通過在數字的末尾附上一個L
來定義長整型,顯然,它比int
類型表示的數字范圍更大。在Python 3里,只有一種整數類型int
,大多數情況下,它很像Python 2里的長整型。由於已經不存在兩種類型的整數,所以就沒有必要使用特殊的語法去區別他們。
Notes | Python 2 | Python 3 |
---|---|---|
① | x =1000000000000L |
x =1000000000000 |
② | x =0xFFFFFFFFFFFFL |
x =0xFFFFFFFFFFFF |
③ | long(x) |
int(x) |
④ | type(x)is long |
type(x)is int |
⑤ | isinstance(x,long) |
isinstance(x,int) |
- 在Python 2里的十進制長整型在Python 3里被替換為十進制的普通整數。
- 在Python 2里的十六進制長整型在Python 3里被替換為十六進制的普通整數。
- 在Python 3里,由於長整型已經不存在了,自然原來的
long()
函數也沒有了。為了強制轉換一個變量到整型,可以使用int()
函數。 - 檢查一個變量是否是整型,獲得它的數據類型,並與一個
int
類型(不是long
)的作比較。 - 你也可以使用
isinstance()
函數來檢查數據類型;再強調一次,使用int
,而不是long
,來檢查整數類型。
<> 比較運算符#
Python 2支持<>
作為!=
的同義詞。Python 3只支持!=
,不再支持<>了。
Notes | Python 2 | Python 3 |
---|---|---|
① | if x <> y: |
if x != y: |
② | if x <> y<> z: |
if x != y!= z: |
- 簡單地比較。
- 相對復雜的三個值之間的比較。
字典類方法has_key()
#
在Python 2里,字典對象的has_key()
方法用來測試字典是否包含特定的鍵(key)。Python 3不再支持這個方法了。你需要使用in
運算符。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_dictionary.has_key('PapayaWhip') |
'PapayaWhip' in a_dictionary |
② | a_dictionary.has_key(x)or a_dictionary.has_key(y) |
x in a_dictionaryor y in a_dictionary |
③ | a_dictionary.has_key(xor y) |
(x or y)in a_dictionary |
④ | a_dictionary.has_key(x+ y) |
(x + y)in a_dictionary |
⑤ | x + a_dictionary.has_key(y) |
x +(y in a_dictionary) |
- 最簡單的形式。
- 運算符
or
的優先級高於運算符in
,所以這里不需要添加括號。 - 另一方面,出於同樣的原因 —
or
的優先級大於in
,這里需要添加括號。(注意:這里的代碼與前面那行完全不同。Python會先解釋x or y
,得到結果x(如果x在布爾上下文里的值是真)或者y。然后Python檢查這個結果是不是a_dictionary的一個鍵。) - 運算符
in
的優先級大於運算符+
,所以代碼里的這種形式從技術上說不需要括號,但是2to3
還是添加了。 - 這種形式一定需要括號,因為
in
的優先級大於+
。
返回列表的字典類方法#
在Python 2里,許多字典類方法的返回值是列表。其中最常用方法的有keys
,items
和values
。在Python 3里,所有以上方法的返回值改為動態視圖(dynamic view)。在一些上下文環境里,這種改變並不會產生影響。如果這些方法的返回值被立即傳遞給另外一個函數,並且那個函數會遍歷整個序列,那么以上方法的返回值是列表或者視圖並不會產生什么不同。在另外一些情況下,Python 3的這些改變干系重大。如果你期待一個能被獨立尋址元素的列表,那么Python 3的這些改變將會使你的代碼卡住(choke),因為視圖(view)不支持索引(indexing)。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_dictionary.keys() |
list(a_dictionary.keys()) |
② | a_dictionary.items() |
list(a_dictionary.items()) |
③ | a_dictionary.iterkeys() |
iter(a_dictionary.keys()) |
④ | [i for iin a_dictionary.iterkeys()] |
[i for iin a_dictionary.keys()] |
⑤ | min(a_dictionary.keys()) |
no change |
- 使用
list()
函數將keys()
的返回值轉換為一個靜態列表,出於安全方面的考量,2to3
可能會報錯。這樣的代碼是有效的,但是對於使用視圖來說,它的效率低一些。你應該檢查轉換后的代碼,看看是否一定需要列表,也許視圖也能完成同樣的工作。 - 這是另外一種視圖(關於
items()
方法的)到列表的轉換。2to3
對values()
方法返回值的轉換也是一樣的。 - Python 3里不再支持
iterkeys()
了。如果必要,使用iter()
將keys()
的返回值轉換成為一個迭代器。 2to3
能夠識別出iterkeys()
方法在列表解析里被使用,然后將它轉換為Python 3里的keys()
方法(不需要使用額外的iter()
去包裝其返回值)。這樣是可行的,因為視圖是可迭代的。2to3
也能識別出keys()
方法的返回值被立即傳給另外一個會遍歷整個序列的函數,所以也就沒有必要先把keys()
的返回值轉換到一個列表。相反的,min()
函數會很樂意遍歷視圖。這個過程對min()
,max()
,sum()
,list()
,tuple()
,set()
,sorted()
,any()
和all()
同樣有效。
被重命名或者重新組織的模塊#
從Python 2到Python 3,標准庫里的一些模塊已經被重命名了。還有一些相互關聯的模塊也被組合或者重新組織,以使得這種關聯更有邏輯性。
http
#
在Python 3里,幾個相關的HTTP模塊被組合成一個單獨的包,即http
。
Notes | Python 2 | Python 3 |
---|---|---|
① | import httplib |
import http.client |
② | import Cookie |
import http.cookies |
③ | import cookielib |
import http.cookiejar |
④ | |
import http.server |
http.client
模塊實現了一個底層的庫,可以用來請求HTTP資源,解析HTTP響應。http.cookies
模塊提供一個蟒樣的(Pythonic)接口來獲取通過HTTP頭部(HTTP header)Set-Cookie發送的cookies- 常用的流行的瀏覽器會把cookies以文件形式存放在磁盤上,
http.cookiejar
模塊可以操作這些文件。 http.server
模塊實現了一個基本的HTTP服務器
urllib
#
Python 2有一些用來分析,編碼和獲取URL的模塊,但是這些模塊就像老鼠窩一樣相互重疊。在Python 3里,這些模塊被重構、組合成了一個單獨的包,即urllib
。
Notes | Python 2 | Python 3 |
---|---|---|
① | import urllib |
import urllib.request, urllib.parse, urllib.error |
② | import urllib2 |
import urllib.request, urllib.error |
③ | import urlparse |
import urllib.parse |
④ | import robotparser |
import urllib.robotparser |
⑤ | |
|
⑥ | |
|
- 以前,Python 2里的
urllib
模塊有各種各樣的函數,包括用來獲取數據的urlopen()
,還有用來將URL分割成其組成部分的splittype()
,splithost()
和splituser()
函數。在新的urllib
包里,這些函數被組織得更有邏輯性。2to3將會修改這些函數的調用以適應新的命名方案。 - 在Python 3里,以前的
urllib2
模塊被並入了urllib
包。同時,以urllib2
里各種你最喜愛的東西將會一個不缺地出現在Python 3的urllib
模塊里,比如build_opener()
方法,Request
對象,HTTPBasicAuthHandler
和friends。 - Python 3里的
urllib.parse
模塊包含了原來Python 2里urlparse
模塊所有的解析函數。 urllib.robotparse
模塊解析robots.txt
文件。- 處理HTTP重定向和其他狀態碼的
FancyURLopener
類在Python 3里的urllib.request
模塊里依然有效。urlencode()
函數已經被轉移到了urllib.parse
里。 Request
對象在urllib.request
里依然有效,但是像HTTPError
這樣的常量已經被轉移到了urllib.error
里。
我是否有提到2to3
也會重寫你的函數調用?比如,如果你的Python 2代碼里導入了urllib
模塊,調用了urllib.urlopen()
函數獲取數據,2to3
會同時修改import
語句和函數調用。
Notes | Python 2 | Python 3 |
---|---|---|
|
|
dbm
#
所有的DBM克隆(DBM clone)現在在單獨的一個包里,即dbm
。如果你需要其中某個特定的變體,比如GNUDBM,你可以導入dbm
包中合適的模塊。
Notes | Python 2 | Python 3 |
---|---|---|
import dbm |
import dbm.ndbm |
|
import gdbm |
import dbm.gnu |
|
import dbhash |
import dbm.bsd |
|
import dumbdbm |
import dbm.dumb |
|
|
import dbm |
xmlrpc
#
XML-RPC是一個通過HTTP協議執行遠程RPC調用的輕重級方法。一些XML-RPC客戶端和XML-RPC服務端的實現庫現在被組合到了獨立的包,即xmlrpc
。
Notes | Python 2 | Python 3 |
---|---|---|
import xmlrpclib |
import xmlrpc.client |
|
|
import xmlrpc.server |
其他模塊#
Notes | Python 2 | Python 3 |
---|---|---|
① | |
import io |
② | |
import pickle |
③ | import __builtin__ |
import builtins |
④ | import copy_reg |
import copyreg |
⑤ | import Queue |
import queue |
⑥ | import SocketServer |
import socketserver |
⑦ | import ConfigParser |
import configparser |
⑧ | import repr |
import reprlib |
⑨ | import commands |
import subprocess |
- 在Python 2里,你通常會這樣做,首先嘗試把
cStringIO
導入作為StringIO
的替代,如果失敗了,再導入StringIO
。不要在Python 3里這樣做;io
模塊會幫你處理好這件事情。它會找出可用的最快實現方法,然后自動使用它。 - 在Python 2里,導入最快的
pickle
實現也是一個與上邊相似的能用方法。在Python 3里,pickle
模塊會自動為你處理,所以不要再這樣做。 builtins
模塊包含了在整個Python語言里都會使用的全局函數,類和常量。重新定義builtins
模塊里的某個函數意味着在每處都重定義了這個全局函數。這聽起來很強大,但是同時也是很可怕的。copyreg
模塊為用C語言定義的用戶自定義類型添加了pickle
模塊的支持。queue
模塊實現一個生產者消費者隊列(multi-producer, multi-consumer queue)。socketserver
模塊為實現各種socket server提供了通用基礎類。configparser
模塊用來解析INI-style配置文件。reprlib
模塊重新實現了內置函數repr()
,並添加了對字符串表示被截斷前長度的控制。subprocess
模塊允許你創建子進程,連接到他們的管道,然后獲取他們的返回值。
包內的相對導入#
包是由一組相關聯的模塊共同組成的單個實體。在Python 2的時候,為了實現同一個包內模塊的相互引用,你會使用import foo
或者from foo import Bar
。Python 2解釋器會先在當前目錄里搜索foo.py
,然后再去Python搜索路徑(sys.path
)里搜索。在Python 3里這個過程有一點不同。Python 3不會首先在當前路徑搜索,它會直接在Python的搜索路徑里尋找。如果你想要包里的一個模塊導入包里的另外一個模塊,你需要顯式地提供兩個模塊的相對路徑。
假設你有如下包,多個文件在同一個目錄下:
chardet/ | +--__init__.py | +--constants.py | +--mbcharsetprober.py | +--universaldetector.py
現在假設universaldetector.py
需要整個導入constants.py
,另外還需要導入mbcharsetprober.py
的一個類。你會怎樣做?
Notes | Python 2 | Python 3 |
---|---|---|
① | import constants |
from .import constants |
② | from mbcharsetproberimport MultiByteCharSetProber |
from .mbcharsetproberimport MultiByteCharsetProber |
- 當你需要從包的其他地方導入整個模塊,使用新的
from . import
語法。這里的句號(.)即表示當前文件(universaldetector.py
)和你想要導入文件(constants.py
)之間的相對路徑。在這個樣例中,這兩個文件在同一個目錄里,所以使用了單個句號。你也可以從父目錄(from .. import anothermodule
)或者子目錄里導入。 - 為了將一個特定的類或者函數從其他模塊里直接導入到你的模塊的名字空間里,在需要導入的模塊名前加上相對路徑,並且去掉最后一個斜線(slash)。在這個例子中,
mbcharsetprober.py
與universaldetector.py
在同一個目錄里,所以相對路徑名就是一個句號。你也可以從父目錄(from .. import anothermodule)或者子目錄里導入。
迭代器方法next()
#
在Python 2里,迭代器有一個next()
方法,用來返回序列里的下一項。在Python 3里這同樣成立,但是現在有了一個新的全局的函數next()
,它使用一個迭代器作為參數。
Notes | Python 2 | Python 3 |
---|---|---|
① | anIterator.next() |
next(anIterator) |
② | a_function_that_returns_an_iterator().next() |
next(a_function_that_returns_an_iterator()) |
③ | |
|
④ | |
no change |
⑤ | |
|
- 最簡單的例子,你不再調用一個迭代器的
next()
方法,現在你將迭代器自身作為參數傳遞給全局函數next()
。 - 假如你有一個返回值是迭代器的函數,調用這個函數然后把結果作為參數傳遞給
next()
函數。(2to3
腳本足夠智能以正確執行這種轉換。) - 假如你想定義你自己的類,然后把它用作一個迭代器,在Python 3里,你可以通過定義特殊方法
__next__()
來實現。 - 如果你定義的類里剛好有一個
next()
,它使用一個或者多個參數,2to3
執行的時候不會動它。這個類不能被當作迭代器使用,因為它的next()
方法帶有參數。 - 這一個有些復雜。如果你恰好有一個叫做next的本地變量,在Python 3里它的優先級會高於全局函數
next()
。在這種情況下,你需要調用迭代器的特別方法__next__()
來獲取序列里的下一個元素。(或者,你也可以重構代碼以使這個本地變量的名字不叫next,但是2to3不會為你做這件事。)
全局函數filter()
#
在Python 2里,filter()
方法返回一個列表,這個列表是通過一個返回值為True
或者False
的函數來檢測序列里的每一項得到的。在Python 3里,filter()
函數返回一個迭代器,不再是列表。
Notes | Python 2 | Python 3 |
---|---|---|
① | filter(a_function, a_sequence) |
list(filter(a_function, a_sequence)) |
② | list(filter(a_function, a_sequence)) |
no change |
③ | filter(None, a_sequence) |
[i for iin a_sequence if i] |
④ | for i in filter(None, a_sequence): |
no change |
⑤ | [i for iin filter(a_function, a_sequence)] |
no change |
- 最簡單的情況下,
2to3
會用一個list()
函數來包裝filter()
,list()
函數會遍歷它的參數然后返回一個列表。 - 然而,如果
filter()
調用已經被list()
包裹,2to3
不會再做處理,因為這種情況下filter()
的返回值是否是一個迭代器是無關緊要的。 - 為了處理
filter(None, ...)
這種特殊的語法,2to3
會將這種調用從語法上等價地轉換為列表解析。 - 由於
for
循環會遍歷整個序列,所以沒有必要再做修改。 - 與上面相同,不需要做修改,因為列表解析會遍歷整個序列,即使
filter()
返回一個迭代器,它仍能像以前的filter()
返回列表那樣正常工作。
全局函數map()
#
跟filter()
作的改變一樣,map()
函數現在返回一個迭代器。(在Python 2里,它返回一個列表。)
Notes | Python 2 | Python 3 |
---|---|---|
① | map(a_function,'PapayaWhip') |
list(map(a_function,'PapayaWhip')) |
② | map(None,'PapayaWhip') |
list('PapayaWhip') |
③ | map(lambda x: x+1, range(42)) |
[x+1for x in range(42)] |
④ | for i in map(a_function, a_sequence): |
no change |
⑤ | [i for iin map(a_function, a_sequence)] |
no change |
- 類似對
filter()
的處理,在最簡單的情況下,2to3
會用一個list()
函數來包裝map()
調用。 - 對於特殊的
map(None, ...)
語法,跟filter(None, ...)
類似,2to3
會將其轉換成一個使用list()
的等價調用 - 如果
map()
的第一個參數是一個lambda函數,2to3
會將其等價地轉換成列表解析。 - 對於會遍歷整個序列的
for
循環,不需要做改變。 - 再一次地,這里不需要做修改,因為列表解析會遍歷整個序列,即使
map()
的返回值是迭代器而不是列表它也能正常工作。
全局函數reduce()
#
在Python 3里,reduce()
函數已經被從全局名字空間里移除了,它現在被放置在fucntools
模塊里。
Notes | Python 2 | Python 3 |
---|---|---|
reduce(a, b, c) |
|
全局函數apply()
#
Python 2有一個叫做apply()
的全局函數,它使用一個函數f和一個列表[a, b, c]
作為參數,返回值是f(a, b, c)
。你也可以通過直接調用這個函數,在列表前添加一個星號(*)作為參數傳遞給它來完成同樣的事情。在Python 3里,apply()
函數不再存在了;必須使用星號標記法。
Notes | Python 2 | Python 3 |
---|---|---|
① | apply(a_function, a_list_of_args) |
a_function(*a_list_of_args) |
② | apply(a_function, a_list_of_args, a_dictionary_of_named_args) |
a_function(*a_list_of_args,**a_dictionary_of_named_args) |
③ | apply(a_function, a_list_of_args+ z) |
a_function(*a_list_of_args+ z) |
④ | apply(aModule.a_function, a_list_of_args) |
aModule.a_function(*a_list_of_args) |
- 最簡單的形式,可以通過在參數列表(就像
[a, b, c]
一樣)前添加一個星號來調用函數。這跟Python 2里的apply()
函數是等價的。 - 在Python 2里,
apply()
函數實際上可以帶3個參數:一個函數,一個參數列表,一個字典命名參數(dictionary of named arguments)。在Python 3里,你可以通過在參數列表前添加一個星號(*
),在字典命名參數前添加兩個星號(**
)來達到同樣的效果。 - 運算符
+
在這里用作連接列表的功能,它的優先級高於運算符*
,所以沒有必要在a_list_of_args + z
周圍添加額外的括號。 2to3
腳本足夠智能來轉換復雜的apply()
調用,包括調用導入模塊里的函數。
全局函數intern()
#
在Python 2里,你可以用intern()
函數作用在一個字符串上來限定(intern)它以達到性能優化。在Python 3里,intern()
函數被轉移到sys
模塊里了。
Notes | Python 2 | Python 3 |
---|---|---|
intern(aString) |
sys.intern(aString) |
exec
語句#
就像print
語句在Python 3里變成了一個函數一樣,exec
語句也是這樣的。exec()
函數使用一個包含任意Python代碼的字符串作為參數,然后就像執行語句或者表達式一樣執行它。exec()
跟eval()
是相似的,但是exec()
更加強大並更具有技巧性。eval()
函數只能執行單獨一條表達式,但是
能夠執行多條語句,導入(import),函數聲明 — 實際上整個Python程序的字符串表示也可以。exec
()
Notes | Python 2 | Python 3 |
---|---|---|
① | exec codeString |
exec(codeString) |
② | exec codeString in a_global_namespace |
exec(codeString, a_global_namespace) |
③ | exec codeString in a_global_namespace, a_local_namespace |
exec(codeString, a_global_namespace, a_local_namespace) |
- 在最簡單的形式下,因為
exec()
現在是一個函數,而不是語句,2to3
會把這個字符串形式的代碼用括號圍起來。 - Python 2里的
exec
語句可以指定名字空間,代碼將在這個由全局對象組成的私有空間里執行。Python 3也有這樣的功能;你只需要把這個名字空間作為第二個參數傳遞給exec()
函數。 - 更加神奇的是,Python 2里的
exec
語句還可以指定一個本地名字空間(比如一個函數里聲明的變量)。在Python 3里,exec()
函數也有這樣的功能。
execfile
語句#
就像以前的exec
語句,Python 2里的execfile
語句也可以像執行Python代碼那樣使用字符串。不同的是exec
使用字符串,而execfile
則使用文件。在Python 3里,execfile
語句已經被去掉了。如果你真的想要執行一個文件里的Python代碼(但是你不想導入它),你可以通過打開這個文件,讀取它的內容,然后調用compile()
全局函數強制Python解釋器編譯代碼,然后調用新的exec()
函數。
Notes | Python 2 | Python 3 |
---|---|---|
execfile('a_filename') |
exec(compile(open('a_filename').read(),'a_filename','exec')) |
repr
(反引號)#
在Python 2里,為了得到一個任意對象的字符串表示,有一種把對象包裝在反引號里(比如`x`
)的特殊語法。在Python 3里,這種能力仍然存在,但是你不能再使用反引號獲得這種字符串表示了。你需要使用全局函數repr()
。
Notes | Python 2 | Python 3 |
---|---|---|
① | `x` |
repr(x) |
② | `'PapayaWhip' + `2`` |
repr('PapayaWhip'+ repr(2)) |
- 記住,x可以是任何東西 — 一個類,函數,模塊,基本數據類型,等等。
repr()
函數可以使用任何類型的參數。 - 在Python 2里,反引號可以嵌套,導致了這種令人費解的(但是有效的)表達式。
2to3
足夠智能以將這種嵌套調用轉換到repr()
函數。
try...except
語句#
從Python 2到Python 3,捕獲異常的語法有些許變化。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
|
② | |
|
③ | |
no change |
④ | |
no change |
- 相對於Python 2里在異常類型后添加逗號,Python 3使用了一個新的關鍵字,
as
。 - 關鍵字
as
也可以用在一次捕獲多種類型異常的情況下。 - 如果你捕獲到一個異常,但是並不在意訪問異常對象本身,Python 2和Python 3的語法是一樣的。
- 類似地,如果你使用一個保險方法(fallback)來捕獲所有異常,Python 2和Python 3的語法是一樣的。
☞在導入模塊(或者其他大多數情況)的時候,你絕對不應該使用這種方法(指以上的fallback)。不然的話,程序可能會捕獲到像
KeyboardInterrupt
(如果用戶按Ctrl-C來中斷程序)這樣的異常,從而使調試變得更加困難。
raise
語句#
Python 3里,拋出自定義異常的語法有細微的變化。
Notes | Python 2 | Python 3 |
---|---|---|
① | raise MyException |
unchanged |
② | raise MyException,'error message' |
raise MyException('error message') |
③ | raise MyException,'error message', a_traceback |
raise MyException('error message').with_traceback(a_traceback) |
④ | raise 'error message' |
unsupported |
- 拋出不帶用戶自定義錯誤信息的異常,這種最簡單的形式下,語法沒有改變。
- 當你想要拋出一個帶用戶自定義錯誤信息的異常時,改變就顯而易見了。Python 2用一個逗號來分隔異常類和錯誤信息;Python 3把錯誤信息作為參數傳遞給異常類。
- Python 2支持一種更加復雜的語法來拋出一個帶用戶自定義回溯(stack trace,堆棧追蹤)的異常。在Python 3里你也可以這樣做,但是語法完全不同。
- 在Python 2里,你可以拋出一個不帶異常類的異常,僅僅只有一個異常信息。在Python 3里,這種形式不再被支持。
2to3
將會警告你它不能自動修復這種語法。
生成器的throw
方法#
在Python 2里,生成器有一個throw()
方法。調用a_generator.throw()
會在生成器被暫停的時候拋出一個異常,然后返回由生成器函數獲取的下一個值。在Python 3里,這種功能仍然可用,但是語法上有一點不同。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_generator.throw(MyException) |
no change |
② | a_generator.throw(MyException,'error message') |
a_generator.throw(MyException('error message')) |
③ | a_generator.throw('error message') |
unsupported |
- 最簡單的形式下,生成器拋出不帶用戶自定義錯誤信息的異常。這種情況下,從Python 2到Python 3語法上沒有變化 。
- 如果生成器拋出一個帶用戶自定義錯誤信息的異常,你需要將這個錯誤信息字符串(error string)傳遞給異常類來以實例化它。
- Python 2還支持拋出只有異常信息的異常。Python 3不支持這種語法,並且
2to3
會顯示一個警告信息,告訴你需要手動地來修復這處代碼。
全局函數xrange()
#
在Python 2里,有兩種方法來獲得一定范圍內的數字:range()
,它返回一個列表,還有range()
,它返回一個迭代器。在Python 3里,range()
返回迭代器,xrange()
不再存在了。
Notes | Python 2 | Python 3 |
---|---|---|
① | xrange(10) |
range(10) |
② | a_list = range(10) |
a_list = list(range(10)) |
③ | [i for iin xrange(10)] |
[i for iin range(10)] |
④ | for i in range(10): |
no change |
⑤ | sum(range(10)) |
no change |
- 在最簡單的情況下,
2to3
會簡單地把xrange()
轉換為range()
。 - 如果你的Python 2代碼使用
range()
,2to3
不知道你是否需要一個列表,或者是否一個迭代器也行。出於謹慎,2to3
可能會報錯,然后使用list()
把range()
的返回值強制轉換為列表類型。 - 如果在列表解析里有
xrange()
函數,就沒有必要將其返回值轉換為一個列表,因為列表解析對迭代器同樣有效。 - 類似的,
for
循環也能作用於迭代器,所以這里也沒有改變任何東西。 - 函數
sum()
能作用於迭代器,所以2to3
也沒有在這里做出修改。就像返回值為視圖(view)而不再是列表的字典類方法一樣,這同樣適用於min()
,max()
,sum()
,list(),tuple()
,set()
,sorted()
,any()
,all()
。
全局函數raw_input()
和input()
#
Python 2有兩個全局函數,用來在命令行請求用戶輸入。第一個叫做input()
,它等待用戶輸入一個Python表達式(然后返回結果)。第二個叫做raw_input()
,用戶輸入什么它就返回什么。這讓初學者非常困惑,並且這被廣泛地看作是Python語言的一個“肉贅”(wart)。Python 3通過重命名raw_input()
為input()
,從而切掉了這個肉贅,所以現在的input()
就像每個人最初期待的那樣工作。
Notes | Python 2 | Python 3 |
---|---|---|
① | raw_input() |
input() |
② | raw_input('prompt') |
input('prompt') |
③ | input() |
eval(input()) |
- 最簡單的形式,
raw_input()
被替換成input()
。 - 在Python 2里,
raw_input()
函數可以指定一個提示符作為參數。Python 3里保留了這個功能。 - 如果你真的想要請求用戶輸入一個Python表達式,計算結果,可以通過調用
input()
函數然后把返回值傳遞給eval()
。
函數屬性func_*
#
在Python 2里,函數的里的代碼可以訪問到函數本身的特殊屬性。在Python 3里,為了一致性,這些特殊屬性被重新命名了。
Notes | Python 2 | Python 3 |
---|---|---|
① | a_function.func_name |
a_function.__name__ |
② | a_function.func_doc |
a_function.__doc__ |
③ | a_function.func_defaults |
a_function.__defaults__ |
④ | a_function.func_dict |
a_function.__dict__ |
⑤ | a_function.func_closure |
a_function.__closure__ |
⑥ | a_function.func_globals |
a_function.__globals__ |
⑦ | a_function.func_code |
a_function.__code__ |
__name__
屬性(原func_name
)包含了函數的名字。__doc__
屬性(原funcdoc
)包含了你在函數源代碼里定義的文檔字符串(docstring)__defaults__
屬性(原func_defaults
)是一個保存參數默認值的元組。__dict__
屬性(原func_dict
)是一個支持任意函數屬性的名字空間。__closure__
屬性(原func_closure
)是一個由cell對象組成的元組,它包含了函數對自由變量(free variable)的綁定。__globals__
屬性(原func_globals
)是一個對模塊全局名字空間的引用,函數本身在這個名字空間里被定義。__code__
屬性(原func_code
)是一個代碼對象,表示編譯后的函數體。
I/O方法xreadlines()
#
在Python 2里,文件對象有一個xreadlines()
方法,它返回一個迭代器,一次讀取文件的一行。這在for
循環中尤其有用。事實上,后來的Python 2版本給文件對象本身添加了這樣的功能。
在Python 3里,xreadlines()
方法不再可用了。2to3
可以解決簡單的情況,但是一些邊緣案例則需要人工介入。
Notes | Python 2 | Python 3 |
---|---|---|
① | for line in a_file.xreadlines(): |
for line in a_file: |
② | for line in a_file.xreadlines(5): |
no change (broken) |
- 如果你以前調用沒有參數的
xreadlines()
,2to3
會把它轉換成文件對象本身。在Python 3里,這種轉換后的代碼可以完成前同樣的工作:一次讀取文件的一行,然后執行for
循環的循環體。 - 如果你以前使用一個參數(每次讀取的行數)調用
xreadlines()
,2to3
不能為你完成從Python 2到Python 3的轉換,你的代碼會以這樣的方式失敗:AttributeError: '_io.TextIOWrapper' object has no attribute 'xreadlines'
。你可以手工的把xreadlines()
改成readlines()
以使代碼能在Python 3下工作。(readline()方法在Python 3里返回迭代器,所以它跟Python 2里的xreadlines()
效率是不相上下的。)
☃
使用元組而非多個參數的lambda
函數#
在Python 2里,你可以定義匿名lambda
函數(anonymous lambda
function),通過指定作為參數的元組的元素個數,使這個函數實際上能夠接收多個參數。事實上,Python 2的解釋器把這個元組“解開”(unpack)成命名參數(named arguments),然后你可以在lambda
函數里引用它們(通過名字)。在Python 3里,你仍然可以傳遞一個元組作為lambda
函數的參數,但是Python解釋器不會把它解析成命名參數。你需要通過位置索引(positional index)來引用每個參數。
Notes | Python 2 | Python 3 |
---|---|---|
① | lambda (x,): x+ f(x) |
lambda x1: x1[0]+ f(x1[0]) |
② | lambda (x, y): x+ f(y) |
lambda x_y: x_y[0]+ f(x_y[1]) |
③ | lambda (x,(y, z)): x+ y + z |
lambda x_y_z: x_y_z[0]+ x_y_z[1][0]+ x_y_z[1][1] |
④ | lambda x, y, z: x+ y + z |
unchanged |
- 如果你已經定義了一個
lambda
函數,它使用包含一個元素的元組作為參數,在Python 3里,它會被轉換成一個包含到x1[0]的引用的lambda
函數。x1是2to3
腳本基於原來元組里的命名參數自動生成的。 - 使用含有兩個元素的元組(x, y)作為參數的
lambda
函數被轉換為x_y,它有兩個位置參數,即x_y[0]和x_y[1]。 2to3
腳本甚至可以處理使用嵌套命名參數的元組作為參數的lambda
函數。產生的結果代碼有點難以閱讀,但是它在Python 3下跟原來的代碼在Python 2下的效果是一樣的。- 你可以定義使用多個參數的
lambda
函數。如果沒有括號包圍在參數周圍,Python 2會把它當作一個包含多個參數的lambda
函數;在這個lambda
函數體里,你通過名字引用這些參數,就像在其他類型的函數里所做的一樣。這種語法在Python 3里仍然有效。
特殊的方法屬性#
在Python 2里,類方法可以訪問到定義他們的類對象(class object),也能訪問方法對象(method object)本身。im_self
是類的實例對象;im_func
是函數對象,im_class
是類本身。在Python 3里,這些屬性被重新命名,以遵循其他屬性的命名約定。
Notes | Python 2 | Python 3 |
---|---|---|
aClassInstance.aClassMethod.im_func |
aClassInstance.aClassMethod.__func__ |
|
aClassInstance.aClassMethod.im_self |
aClassInstance.aClassMethod.__self__ |
|
aClassInstance.aClassMethod.im_class |
aClassInstance.aClassMethod.__self__.__class__ |
__nonzero__
特殊方法#
在Python 2里,你可以創建自己的類,並使他們能夠在布爾上下文(boolean context)中使用。舉例來說,你可以實例化這個類,並把這個實例對象用在一個if
語句中。為了實現這個目的,你定義一個特別的__nonzero__()
方法,它的返回值為True
或者False
,當實例對象處在布爾上下文中的時候這個方法就會被調用 。在Python 3里,你仍然可以完成同樣的功能,但是這個特殊方法的名字變成了__bool__()
。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
|
② | |
no change |
- 當在布爾上下文使用一個類對象時,Python 3會調用
__bool__()
,而非__nonzero__()
。 - 然而,如果你有定義了一個使用兩個參數的
__nonzero__()
方法,2to3
腳本會假設你定義的這個方法有其他用處,因此不會對代碼做修改。
八進制類型#
在Python 2和Python 3之間,定義八進制(octal)數的語法有輕微的改變。
Notes | Python 2 | Python 3 |
---|---|---|
x =0755 |
x =0o755 |
sys.maxint
#
由於長整型和整型被整合在一起了,sys.maxint
常量不再精確。但是因為這個值對於檢測特定平台的能力還是有用處的,所以它被Python 3保留,並且重命名為sys.maxsize
。
Notes | Python 2 | Python 3 |
---|---|---|
① | from sys importmaxint |
from sys importmaxsize |
② | a_function(sys.maxint) |
a_function(sys.maxsize) |
maxint
變成了maxsize
。- 所有的
sys.maxint
都變成了sys.maxsize
。
全局函數callable()
#
在Python 2里,你可以使用全局函數callable()
來檢查一個對象是否可調用(callable,比如函數)。在Python 3里,這個全局函數被取消了。為了檢查一個對象是否可調用,可以檢查特殊方法__call__()
的存在性。
Notes | Python 2 | Python 3 |
---|---|---|
callable(anything) |
hasattr(anything,'__call__') |
全局函數zip()
#
在Python 2里,全局函數zip()
可以使用任意多個序列作為參數,它返回一個由元組構成的列表。第一個元組包含了每個序列的第一個元素;第二個元組包含了每個序列的第二個元素;依次遞推下去。在Python 3里,zip()
返回一個迭代器,而非列表。
Notes | Python 2 | Python 3 |
---|---|---|
① | zip(a, b, c) |
list(zip(a, b, c)) |
② | d.join(zip(a, b, c)) |
no change |
- 最簡單的形式,你可以通過調用
list()
函數包裝zip()
的返回值來恢復zip()
函數以前的功能,list()
函數會遍歷這個zip()
函數返回的迭代器,然后返回結果的列表表示。 - 在已經會遍歷序列所有元素的上下文環境里(比如這里對
join()
方法的調用),zip()
返回的迭代器能夠正常工作。2to3
腳本會檢測到這些情況,不會對你的代碼作出改變。
StandardError
異常#
在Python 2里,StandardError
是除了StopIteration
,GeneratorExit
,KeyboardInterrupt
,SystemExit
之外所有其他內置異常的基類。在Python 3里,StandardError
已經被取消了;使用Exception
替代。
Notes | Python 2 | Python 3 |
---|---|---|
x =StandardError() |
x =Exception() |
|
x =StandardError(a, b, c) |
x =Exception(a, b, c) |
types
模塊中的常量#
types
模塊里各種各樣的常量能幫助你決定一個對象的類型。在Python 2里,它包含了代表所有基本數據類型的常量,如dict
和int
。在Python 3里,這些常量被已經取消了。只需要使用基礎類型的名字來替代。
Notes | Python 2 | Python 3 |
---|---|---|
types.UnicodeType |
str |
|
types.StringType |
bytes |
|
types.DictType |
dict |
|
types.IntType |
int |
|
types.LongType |
int |
|
types.ListType |
list |
|
types.NoneType |
type(None) |
|
types.BooleanType |
bool |
|
types.BufferType |
memoryview |
|
types.ClassType |
type |
|
types.ComplexType |
complex |
|
types.EllipsisType |
type(Ellipsis) |
|
types.FloatType |
float |
|
types.ObjectType |
object |
|
types.NotImplementedType |
type(NotImplemented) |
|
types.SliceType |
slice |
|
types.TupleType |
tuple |
|
types.TypeType |
type |
|
types.XRangeType |
range |
☞
types.StringType
被映射為bytes
,而非str
,因為Python 2里的“string”(非Unicode編碼的字符串,即普通字符串)事實上只是一些使用某種字符編碼的字節序列(a sequence of bytes)。
全局函數isinstance()
#
isinstance()
函數檢查一個對象是否是一個特定類(class)或者類型(type)的實例。在Python 2里,你可以傳遞一個由類型(types)構成的元組給isinstance()
,如果該對象是元組里的任意一種類型,函數返回True
。在Python 3里,你依然可以這樣做,但是不推薦使用把一種類型作為參數傳遞兩次。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x,(int,float,int)) |
isinstance(x,(int,float)) |
basestring
數據類型#
Python 2有兩種字符串類型:Unicode編碼的字符串和非Unicode編碼的字符串。但是其實還有另外 一種類型,即basestring
。它是一個抽象數據類型,是str
和unicode
類型的超類(superclass)。它不能被直接調用或者實例化,但是你可以把它作為isinstance()
的參數來檢測一個對象是否是一個Unicode字符串或者非Unicode字符串。在Python 3里,只有一種字符串類型,所以basestring
就沒有必要再存在了。
Notes | Python 2 | Python 3 |
---|---|---|
isinstance(x, basestring) |
isinstance(x, str) |
itertools
模塊#
Python 2.3引入了itertools
模塊,它定義了全局函數zip()
,map()
,filter()
的變體(variant),這些變體的返回類型為迭代器,而非列表。在Python 3里,由於這些全局函數的返回類型本來就是迭代器,所以這些itertools
里的這些變體函數就被取消了。(在itertools
模塊里仍然還有許多其他的有用的函數,而不僅僅是以上列出的這些。)
Notes | Python 2 | Python 3 |
---|---|---|
① | itertools.izip(a, b) |
zip(a, b) |
② | itertools.imap(a, b) |
map(a, b) |
③ | itertools.ifilter(a, b) |
filter(a, b) |
④ | from itertools import imap, izip, foo |
from itertools import foo |
- 使用全局的
zip()
函數,而非itertools.izip()
。 - 使用
map()
而非itertools.imap()
。 itertools.ifilter()
變成了filter()
。itertools
模塊在Python 3里仍然存在,它只是不再包含那些已經轉移到全局名字空間的函數。2to3
腳本能夠足夠智能地去移除那些不再有用的導入語句,同時保持其他的導入語句的完整性。
sys.exc_type
, sys.exc_value
, sys.exc_traceback
#
處理異常的時候,在sys
模塊里有三個你可以訪問的變量:sys.exc_type,
sys.exc_value,
sys.exc_traceback
。(實際上這些在Python 1的時代就有。)從Python 1.5開始,由於新出的sys.exc_info
,不再推薦使用這三個變量了,這是一個包含所有以上三個元素的元組。在Python 3里,這三個變量終於不再存在了;這意味着,你必須使用sys.exc_info
。
Notes | Python 2 | Python 3 |
---|---|---|
sys.exc_type |
sys.exc_info()[0] |
|
sys.exc_value |
sys.exc_info()[1] |
|
sys.exc_traceback |
sys.exc_info()[2] |
對元組的列表解析#
在Python 2里,如果你需要編寫一個遍歷元組的列表解析,你不需要在元組值的周圍加上括號。在Python 3里,這些括號是必需的。
Notes | Python 2 | Python 3 |
---|---|---|
[i for iin 1,2] |
[i for iin (1,2)] |
os.getcwdu()
函數#
Python 2有一個叫做os.getcwd()
的函數,它將當前的工作目錄作為一個(非Unicode編碼的)字符串返回。由於現代的文件系統能夠處理能何字符編碼的目錄名,Python 2.3引入了os.getcwdu()
函數。os.getcwdu()
函數把當前工作目錄用Unicode編碼的字符串返回。在Python 3里,由於只有一種字符串類型(Unicode類型的),所以你只需要os.getcwd()
就可以了。
Notes | Python 2 | Python 3 |
---|---|---|
os.getcwdu() |
os.getcwd() |
元類(metaclass)#
在Python 2里,你可以通過在類的聲明中定義metaclass
參數,或者定義一個特殊的類級別的(class-level)__metaclass__
屬性,來創建元類。在Python 3里,__metaclass__
屬性已經被取消了。
Notes | Python 2 | Python 3 |
---|---|---|
① | |
unchanged |
② | |
|
③ | |
|
- 在聲明類的時候聲明
metaclass
參數,這在Python 2和Python 3里都有效,它們是一樣的。 - 在類的定義里聲明
__metaclass__
屬性在Python 2里有效,但是在Python 3里不再有效。 2to3
能夠構建一個有效的類聲明,即使這個類繼承自多個父類。
關於代碼風格#
以下所列的“修補”(fixes)實質上並不算真正的修補。意思就是,他們只是代碼的風格上的事情,而不涉及到代碼的本質。但是Python的開發者們在使得代碼風格盡可能一致方面非常有興趣(have a vested interest)。為此,有一個專門o描述Python代碼風格的官方指導手冊 — 細致到能使人痛苦 — 都是一些你不太可能關心的在各種各樣的細節上的挑剔。鑒於2to3
為轉換代碼提供了一個這么好的條件,腳本的作者們添加了一些可選的特性以使你的代碼更具可讀性。
set()
字面值(literal)(顯式的)#
在Python 2城,定義一個字面值集合(literal set)的唯一方法就是調用set(a_sequence)
。在Python 3里這仍然有效,但是使用新的標注記號(literal notation):大括號({})是一種更清晰的方法。這種方法除了空集以外都有效,因為字典也用大括號標記,所以{}
表示一個空的字典,而不是一個空集。
☞
2to3
腳本默認不會修復set()
字面值。為了開啟這個功能,在命令行調用2to3
的時候指定-f set_literal參數。
Notes | Before | After |
---|---|---|
set([1,2, 3]) |
{1,2, 3} |
|
set((1,2, 3)) |
{1,2, 3} |
|
set([ifor i in a_sequence]) |
{i for iin a_sequence} |
全局函數buffer()
(顯式的)#
用C實現的Python對象可以導出一個“緩沖區接口”(buffer interface),它允許其他的Python代碼直接讀寫一塊內存。(這聽起來很強大,它也同樣可怕。)在Python 3里,buffer()
被重新命名為memoryview()
。(實際的修改更加復雜,但是你幾乎可以忽略掉這些不同之處。)
☞
2to3
腳本默認不會修復buffer()
函數。為了開啟這個功能,在命令行調用2to3
的時候指定-f buffer參數。
Notes | Before | After |
---|---|---|
x =buffer(y) |
x =memoryview(y) |
逗號周圍的空格(顯式的)#
盡管Python對用於縮進和凸出(indenting and outdenting)的空格要求很嚴格,但是對於空格在其他方面的使用Python還是很自由的。在列表,元組,集合和字典里,空格可以出現在逗號的前面或者后面,這不會有什么壞影響。但是,Python代碼風格指導手冊上指出,逗號前不能有空格,逗號后應該包含一個空格。盡管這純粹只是一個美觀上的考量(代碼仍然可以正常工作,在Python 2和Python 3里都可以),但是2to3
腳本可以依據手冊上的標准為你完成這個修復。
☞
2to3
腳本默認不會修復逗號周圍的空格。為了開啟這個功能,在命令行調用2to3
的時候指定-f wscomma參數。
Notes | Before | After |
---|---|---|
a ,b |
a, b |
|
{a :b} |
{a: b} |
慣例(Common idioms)(顯式的)#
在Python社區里建立起來了許多慣例。有一些比如while 1:
loop,它可以追溯到Python 1。(Python直到Python 2.3才有真正意義上的布爾類型,所以開發者以前使用1
和0
替代。)當代的Python程序員應該鍛煉他們的大腦以使用這些慣例的現代版。
☞
2to3
腳本默認不會為這些慣例做修復。為了開啟這個功能,在命令行調用2to3
的時候指定-f idioms參數。
Notes | Before | After |
---|---|---|
|
|
|
type(x)== T |
isinstance(x, T) |
|
type(x)is T |
isinstance(x, T) |
|
|
|