因爲Sublime 3 使用Python3,不得不將代碼一直到python 3上面,差別還很多啊!html
幾乎全部的Python 2程序都須要一些修改才能正常地運行在Python 3的環境下。爲了簡化這個轉換過程,Python 3自帶了一個叫作2to3
的實用腳本(Utility Script),這個腳本會將你的Python 2程序源文件做爲輸入,而後自動將其轉換到Python 3的形式。案例研究:將chardet
移植到Python 3(porting chardet to Python 3)描述瞭如何運行這個腳本,而後展現了一些它不能自動修復的狀況。這篇附錄描述了它可以自動修復的內容。python
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()
輸出到一個另一個管道。cookie
Python 2有兩種字符串類型:Unicode字符串和非Unicode字符串。Python 3只有一種類型:Unicode字符串(Unicode strings)。app
Notes | Python 2 | Python 3 |
---|---|---|
① | u'PapayaWhip' |
'PapayaWhip' |
② | ur'PapayaWhip\foo' |
r'PapayaWhip\foo' |
Python 2裏的Unicode字符串在Python 3裏即普通字符串,由於在Python 3裏字符串老是Unicode形式的。socket
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_dictionary or y in a_dictionary |
③ | a_dictionary.has_key(x or 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 i in a_dictionary.iterkeys()] |
[i for i in 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 BaseHTTPServer |
import http.server |
http.client
模塊實現了一個底層的庫,能夠用來請求HTTP資源,解析HTTP響應。
http.cookies
模塊提供一個蟒樣的(Pythonic)接口來獲取經過HTTP頭部(HTTPheader)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 |
⑤ | from urllib import FancyURLopener |
from urllib.request import FancyURLopener |
⑥ | from urllib2 import Request |
from urllib.request import Request |
之前,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 |
---|---|---|
import urllib |
import urllib.request, urllib.parse, urllib.error |
dbm
#全部的DBM克隆(DBMclone)如今在單獨的一個包裏,即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 anydbm |
import dbm |
xmlrpc
#XML-RPC是一個經過HTTP協議執行遠程RPC調用的輕重級方法。一些XML-RPC客戶端和XML-RPC服務端的實現庫如今被組合到了獨立的包,即xmlrpc
。
Notes | Python 2 | Python 3 |
---|---|---|
import xmlrpclib |
import xmlrpc.client |
|
import DocXMLRPCServer |
import xmlrpc.server |
Notes | Python 2 | Python 3 |
---|---|---|
① | try: |
import io |
② | try: |
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 mbcharsetprober import MultiByteCharSetProber |
from .mbcharsetprober import 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()) |
③ | class A: |
class A: |
④ | class A: |
no change |
⑤ | next = 42 |
next = 42 |
最簡單的例子,你再也不調用一個迭代器的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 i in a_sequence if i] |
④ | for i in filter(None, a_sequence): |
no change |
⑤ | [i for i in 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+1 for x in range(42)] |
④ | for i in map(a_function, a_sequence): |
no change |
⑤ | [i for i in 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) |
from functools import reduce |
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 |
---|---|---|
① | try: |
try: |
② | try: |
try: |
③ | try: |
no change |
④ | try: |
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 i in xrange(10)] |
[i for i in 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
)是一個代碼對象,表示編譯後的函數體。
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 |
---|---|---|
① | class A: |
class A: |
② | class A: |
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 import maxint |
from sys import maxsize |
② | 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 i in 1, 2] |
[i for i in (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 |
---|---|---|
① | class C(metaclass=PapayaMeta): |
unchanged |
② | class Whip: |
class Whip(metaclass=PapayaMeta): |
③ | class C(Whipper, Beater): |
class C(Whipper, Beater, metaclass=PapayaMeta): |
在聲明類的時候聲明
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([i for i in a_sequence]) |
{i for i in 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 |
---|---|---|
while 1: |
while True: |
|
type(x) == T |
isinstance(x, T) |
|
type(x) is T |
isinstance(x, T) |
|
a_list = list(a_sequence) |
a_list = sorted(a_sequence)do_stuff(a_list) |