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