使用 2to3 將代碼移植到 Python 3

 

print語句#

在Python 2裏,print是一個語句。不管你想輸出什麼,只要將它們放在print關鍵字後邊就能夠。在Python 3裏,print()是一個函數。就像其餘的函數同樣,print()須要你將想要輸出的東西做爲參數傳給它。html

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)

  1. 爲輸出一個空白行,須要調用不帶參數的print()
  2. 爲輸出一個單獨的值,須要將這這個值做爲print()的一個參數就能夠了。
  3. 爲輸出使用一個空格分隔的兩個值,用兩個參數調用print()便可。
  4. 這個例子有一些技巧。在Python 2裏,若是你使用一個逗號(,)做爲print語句的結尾,它將會用空格分隔輸出的結果,而後在輸出一個尾隨的空格(trailing space),而不輸出回車(carriage return)。在Python 3裏,經過把end=' '做爲一個關鍵字參數傳給print()能夠實現一樣的效果。參數end的默認值爲'\n',因此經過從新指定end參數的值,能夠取消在末尾輸出回車符。
  5. 在Python 2裏,你能夠經過使用>>pipe_name語法,把輸出重定向到一個管道,好比sys.stderr。在Python 3裏,你能夠經過將管道做爲關鍵字參數file的值傳遞給print()來完成一樣的功能。參數file的默認值爲std.stdout,因此從新指定它的值將會使print()輸出到一個另一個管道。

Unicode字符串#

Python 2有兩種字符串類型:Unicode字符串和非Unicode字符串。Python 3只有一種類型:Unicode字符串(Unicode strings)python

Notes Python 2 Python 3
u'PapayaWhip' 'PapayaWhip'
ur'PapayaWhip\foo' r'PapayaWhip\foo'

  1. Python 2裏的Unicode字符串在Python 3裏即普通字符串,由於在Python 3裏字符串老是Unicode形式的。
  2. 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有爲非浮點數準備的intlong類型。int類型的最大值不能超過sys.maxint,並且這個最大值是平臺相關的。能夠經過在數字的末尾附上一個L來定義長整型,顯然,它比int類型表示的數字範圍更大。在Python 3裏,只有一種整數類型int,大多數狀況下,它很像Python 2裏的長整型。因爲已經不存在兩種類型的整數,因此就沒有必要使用特殊的語法去區別他們。web

進一步閱讀:PEP 237:統一長整型和整型瀏覽器

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)

  1. 在Python 2裏的十進制長整型在Python 3裏被替換爲十進制的普通整數。
  2. 在Python 2裏的十六進制長整型在Python 3裏被替換爲十六進制的普通整數。
  3. 在Python 3裏,因爲長整型已經不存在了,天然原來的long()函數也沒有了。爲了強制轉換一個變量到整型,可使用int()函數。
  4. 檢查一個變量是不是整型,得到它的數據類型,並與一個int類型(不是long)的做比較。
  5. 你也可使用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:

  1. 簡單地比較。
  2. 相對複雜的三個值之間的比較。

字典類方法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)

  1. 最簡單的形式。
  2. 運算符or的優先級高於運算符in,因此這裏不須要添加括號。
  3. 另外一方面,出於一樣的緣由 — or的優先級大於in,這裏須要添加括號。(注意:這裏的代碼與前面那行徹底不一樣。Python會先解釋x or y,獲得結果x(若是x在布爾上下文裏的值是真)或者y。而後Python檢查這個結果是否是a_dictionary的一個鍵。)
  4. 運算符in的優先級大於運算符+,因此代碼裏的這種形式從技術上說不須要括號,可是2to3仍是添加了。
  5. 這種形式必定須要括號,由於in的優先級大於+

返回列表的字典類方法#

在Python 2裏,許多字典類方法的返回值是列表。其中最經常使用方法的有keysitemsvalues。在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

  1. 使用list()函數將keys()的返回值轉換爲一個靜態列表,出於安全方面的考量,2to3可能會報錯。這樣的代碼是有效的,可是對於使用視圖來講,它的效率低一些。你應該檢查轉換後的代碼,看看是否必定須要列表,也許視圖也能完成一樣的工做。
  2. 這是另一種視圖(關於items()方法的)到列表的轉換。2to3values()方法返回值的轉換也是同樣的。
  3. Python 3裏再也不支持iterkeys()了。若是必要,使用iter()keys()的返回值轉換成爲一個迭代器。
  4. 2to3可以識別出iterkeys()方法在列表解析裏被使用,而後將它轉換爲Python 3裏的keys()方法(不須要使用額外的iter()去包裝其返回值)。這樣是可行的,由於視圖是可迭代的。
  5. 2to3也能識別出keys()方法的返回值被當即傳給另一個會遍歷整個序列的函數,因此也就沒有必要先把keys()的返回值轉換到一個列表。相反的,min()函數會很樂意遍歷視圖。這個過程對min()max()sum()list()tuple()set()sorted()any()all()一樣有效。

被重命名或者從新組織的模塊#

從Python 2到Python 3,標準庫裏的一些模塊已經被重命名了。還有一些相互關聯的模塊也被組合或者從新組織,以使得這種關聯更有邏輯性。cookie

http#

在Python 3裏,幾個相關的HTTP模塊被組合成一個單獨的包,即httpapp

Notes Python 2 Python 3
import httplib import http.client
import Cookie import http.cookies
import cookielib import http.cookiejar
import BaseHTTPServerimport SimpleHTTPServerimport CGIHttpServer
import http.server

  1. http.client模塊實現了一個底層的庫,能夠用來請求HTTP資源,解析HTTP響應。
  2. http.cookies模塊提供一個蟒樣的(Pythonic)接口來獲取經過HTTP頭部(HTTP header)Set-Cookie發送的cookies
  3. 經常使用的流行的瀏覽器會把cookies以文件形式存放在磁盤上,http.cookiejar模塊能夠操做這些文件。
  4. 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 FancyURLopenerfrom urllib import urlencode
from urllib.request import FancyURLopenerfrom urllib.parse import urlencode
from urllib2 import Requestfrom urllib2 import HTTPError
from urllib.request import Requestfrom urllib.error import HTTPError

  1. 之前,Python 2裏的urllib模塊有各類各樣的函數,包括用來獲取數據的urlopen(),還有用來將URL分割成其組成部分的splittype()splithost()splituser()函數。在新的urllib包裏,這些函數被組織得更有邏輯性。2to3將會修改這些函數的調用以適應新的命名方案。
  2. 在Python 3裏,之前的urllib2模塊被併入了urllib包。同時,以urllib2裏各類你最喜好的東西將會一個不缺地出如今Python 3的urllib模塊裏,好比build_opener()方法,Request對象,HTTPBasicAuthHandler和friends。
  3. Python 3裏的urllib.parse模塊包含了原來Python 2裏urlparse模塊全部的解析函數。
  4. urllib.robotparse模塊解析robots.txt文件
  5. 處理HTTP重定向和其餘狀態碼的FancyURLopener類在Python 3裏的urllib.request模塊裏依然有效。urlencode()函數已經被轉移到了urllib.parse裏。
  6. Request對象在urllib.request裏依然有效,可是像HTTPError這樣的常量已經被轉移到了urllib.error裏。

我是否有提到2to3也會重寫你的函數調用?好比,若是你的Python 2代碼裏導入了urllib模塊,調用了urllib.urlopen()函數獲取數據,2to3會同時修改import語句和函數調用。

Notes Python 2 Python 3
 
import urllibprint urllib.urlopen('http://diveintopython3.org/').read()
import urllib.request, urllib.parse, urllib.errorprint(urllib.request.urlopen('http://diveintopython3.org/').read())

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 anydbmimport whichdb
import dbm

xmlrpc#

XML-RPC是一個經過HTTP協議執行遠程RPC調用的輕重級方法。一些XML-RPC客戶端和XML-RPC服務端的實現庫如今被組合到了獨立的包,即xmlrpc

Notes Python 2 Python 3
  import xmlrpclib import xmlrpc.client
 
import DocXMLRPCServerimport SimpleXMLRPCServer
import xmlrpc.server

其餘模塊#

Notes Python 2 Python 3
try:    import cStringIO as StringIOexcept ImportError:    import StringIO
import io
try:    import cPickle as pickleexcept 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

  1. 在Python 2裏,你一般會這樣作,首先嚐試把cStringIO導入做爲StringIO的替代,若是失敗了,再導入StringIO。不要在Python 3裏這樣作;io模塊會幫你處理好這件事情。它會找出可用的最快實現方法,而後自動使用它。
  2. 在Python 2裏,導入最快的pickle實現也是一個與上邊類似的能用方法。在Python 3裏,pickle模塊會自動爲你處理,因此不要再這樣作。
  3. builtins模塊包含了在整個Python語言裏都會使用的全局函數,類和常量。從新定義builtins模塊裏的某個函數意味着在每處都重定義了這個全局函數。這聽起來很強大,可是同時也是很可怕的。
  4. copyreg模塊爲用C語言定義的用戶自定義類型添加了pickle模塊的支持。
  5. queue模塊實現一個生產者消費者隊列(multi-producer, multi-consumer queue)。
  6. socketserver模塊爲實現各類socket server提供了通用基礎類。
  7. configparser模塊用來解析INI-style配置文件。
  8. reprlib模塊從新實現了內置函數repr(),並添加了對字符串表示被截斷前長度的控制。
  9. 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 mbcharsetproberimportMultiByteCharSetProber from .mbcharsetproberimportMultiByteCharsetProber

  1. 當你須要從包的其餘地方導入整個模塊,使用新的from . import語法。這裏的句號(.)即表示當前文件(universaldetector.py)和你想要導入文件(constants.py)之間的相對路徑。在這個樣例中,這兩個文件在同一個目錄裏,因此使用了單個句號。你也能夠從父目錄(from .. import anothermodule)或者子目錄裏導入。
  2. 爲了將一個特定的類或者函數從其餘模塊裏直接導入到你的模塊的名字空間裏,在須要導入的模塊名前加上相對路徑,而且去掉最後一個斜線(slash)。在這個例子中,mbcharsetprober.pyuniversaldetector.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:    def next(self):        pass
class A:    def __next__(self):        pass
class A:    def next(self, x, y):        pass
no change
next = 42for an_iterator in a_sequence_of_iterators:    an_iterator.next()
next = 42for an_iterator in a_sequence_of_iterators:    an_iterator.__next__()

  1. 最簡單的例子,你再也不調用一個迭代器的next()方法,如今你將迭代器自身做爲參數傳遞給全局函數next()
  2. 假如你有一個返回值是迭代器的函數,調用這個函數而後把結果做爲參數傳遞給next()函數。(2to3腳本足夠智能以正確執行這種轉換。)
  3. 假如你想定義你本身的類,而後把它用做一個迭代器,在Python 3裏,你能夠經過定義特殊方法__next__()來實現。
  4. 若是你定義的類裏恰好有一個next(),它使用一個或者多個參數,2to3執行的時候不會動它。這個類不能被看成迭代器使用,由於它的next()方法帶有參數。
  5. 這一個有些複雜。若是你剛好有一個叫作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

  1. 最簡單的狀況下,2to3會用一個list()函數來包裝filter()list()函數會遍歷它的參數而後返回一個列表。
  2. 然而,若是filter()調用已經被list()包裹,2to3不會再作處理,由於這種狀況下filter()的返回值是不是一個迭代器是可有可無的。
  3. 爲了處理filter(None, ...)這種特殊的語法,2to3會將這種調用從語法上等價地轉換爲列表解析。
  4. 因爲for循環會遍歷整個序列,因此沒有必要再作修改。
  5. 與上面相同,不須要作修改,由於列表解析會遍歷整個序列,即便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

  1. 相似對filter()的處理,在最簡單的狀況下,2to3會用一個list()函數來包裝map()調用。
  2. 對於特殊的map(None, ...)語法,跟filter(None, ...)相似,2to3會將其轉換成一個使用list()的等價調用
  3. 若是map()的第一個參數是一個lambda函數,2to3會將其等價地轉換成列表解析。
  4. 對於會遍歷整個序列的for循環,不須要作改變。
  5. 再一次地,這裏不須要作修改,由於列表解析會遍歷整個序列,即便map()的返回值是迭代器而不是列表它也能正常工做。

全局函數reduce()#

在Python 3裏,reduce()函數已經被從全局名字空間裏移除了,它如今被放置在fucntools模塊裏。

Notes Python 2 Python 3
  reduce(a, b, c)
from functools import reduce 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)

  1. 最簡單的形式,能夠經過在參數列表(就像[a, b, c]同樣)前添加一個星號來調用函數。這跟Python 2裏的apply()函數是等價的。
  2. 在Python 2裏,apply()函數實際上能夠帶3個參數:一個函數,一個參數列表,一個字典命名參數(dictionary of named arguments)。在Python 3裏,你能夠經過在參數列表前添加一個星號(*),在字典命名參數前添加兩個星號(**)來達到一樣的效果。
  3. 運算符+在這裏用做鏈接列表的功能,它的優先級高於運算符*,因此沒有必要在a_list_of_args + z周圍添加額外的括號。
  4. 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()函數只能執行單獨一條表達式,可是exec()可以執行多條語句,導入(import),函數聲明 — 實際上整個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)

  1. 在最簡單的形式下,由於exec()如今是一個函數,而不是語句,2to3會把這個字符串形式的代碼用括號圍起來。
  2. Python 2裏的exec語句能夠指定名字空間,代碼將在這個由全局對象組成的私有空間裏執行。Python 3也有這樣的功能;你只須要把這個名字空間做爲第二個參數傳遞給exec()函數。
  3. 更加神奇的是,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))

  1. 記住,x能夠是任何東西 — 一個類,函數,模塊,基本數據類型,等等。repr()函數可使用任何類型的參數。
  2. 在Python 2裏,反引號能夠嵌套,致使了這種使人費解的(可是有效的)表達式。2to3足夠智能以將這種嵌套調用轉換到repr()函數。

try...except語句#

從Python 2到Python 3,捕獲異常的語法有些許變化。

Notes Python 2 Python 3
try:    import mymoduleexcept ImportError, e     pass
try:    import mymoduleexcept ImportError as e:    pass
try:    import mymoduleexcept (RuntimeError, ImportError), e     pass
try:    import mymoduleexcept (RuntimeError, ImportError) as e:    pass
try:    import mymoduleexcept ImportError:    pass
no change
try:    import mymoduleexcept:    pass
no change

  1. 相對於Python 2裏在異常類型後添加逗號,Python 3使用了一個新的關鍵字,as
  2. 關鍵字as也能夠用在一次捕獲多種類型異常的狀況下。
  3. 若是你捕獲到一個異常,可是並不在乎訪問異常對象自己,Python 2和Python 3的語法是同樣的。
  4. 相似地,若是你使用一個保險方法(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

  1. 拋出不帶用戶自定義錯誤信息的異常,這種最簡單的形式下,語法沒有改變。
  2. 當你想要拋出一個帶用戶自定義錯誤信息的異常時,改變就顯而易見了。Python 2用一個逗號來分隔異常類和錯誤信息;Python 3把錯誤信息做爲參數傳遞給異常類。
  3. Python 2支持一種更加複雜的語法來拋出一個帶用戶自定義回溯(stack trace,堆棧追蹤)的異常。在Python 3裏你也能夠這樣作,可是語法徹底不一樣。
  4. 在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

  1. 最簡單的形式下,生成器拋出不帶用戶自定義錯誤信息的異常。這種狀況下,從Python 2到Python 3語法上沒有變化 。
  2. 若是生成器拋出一個帶用戶自定義錯誤信息的異常,你須要將這個錯誤信息字符串(error string)傳遞給異常類來以實例化它。
  3. 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

  1. 在最簡單的狀況下,2to3會簡單地把xrange()轉換爲range()
  2. 若是你的Python 2代碼使用range()2to3不知道你是否須要一個列表,或者是否一個迭代器也行。出於謹慎,2to3可能會報錯,而後使用list()range()的返回值強制轉換爲列表類型。
  3. 若是在列表解析裏有xrange()函數,就沒有必要將其返回值轉換爲一個列表,由於列表解析對迭代器一樣有效。
  4. 相似的,for循環也能做用於迭代器,因此這裏也沒有改變任何東西。
  5. 函數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())

  1. 最簡單的形式,raw_input()被替換成input()
  2. 在Python 2裏,raw_input()函數能夠指定一個提示符做爲參數。Python 3裏保留了這個功能。
  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__

  1. __name__屬性(原func_name)包含了函數的名字。
  2. __doc__屬性(原funcdoc)包含了你在函數源代碼裏定義的文檔字符串(docstring)
  3. __defaults__屬性(原func_defaults)是一個保存參數默認值的元組。
  4. __dict__屬性(原func_dict)是一個支持任意函數屬性的名字空間。
  5. __closure__屬性(原func_closure)是一個由cell對象組成的元組,它包含了函數對自由變量(free variable)的綁定。
  6. __globals__屬性(原func_globals)是一個對模塊全局名字空間的引用,函數自己在這個名字空間裏被定義。
  7. __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)

  1. 若是你之前調用沒有參數的xreadlines()2to3會把它轉換成文件對象自己。在Python 3裏,這種轉換後的代碼能夠完成前一樣的工做:一次讀取文件的一行,而後執行for循環的循環體。
  2. 若是你之前使用一個參數(每次讀取的行數)調用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

  1. 若是你已經定義了一個lambda函數,它使用包含一個元素的元組做爲參數,在Python 3裏,它會被轉換成一個包含到x1[0]的引用的lambda函數。x1是2to3腳本基於原來元組裏的命名參數自動生成的。
  2. 使用含有兩個元素的元組(x, y)做爲參數的lambda函數被轉換爲x_y,它有兩個位置參數,即x_y[0]和x_y[1]。
  3. 2to3腳本甚至能夠處理使用嵌套命名參數的元組做爲參數的lambda函數。產生的結果代碼有點難以閱讀,可是它在Python 3下跟原來的代碼在Python 2下的效果是同樣的。
  4. 你能夠定義使用多個參數的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:    def __nonzero__(self):        pass
class A:    def __bool__(self):        pass
class A:    def __nonzero__(self, x, y):        pass
no change

  1. 當在布爾上下文使用一個類對象時,Python 3會調用__bool__(),而非__nonzero__()
  2. 然而,若是你有定義了一個使用兩個參數的__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)

  1. maxint變成了maxsize
  2. 全部的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

  1. 最簡單的形式,你能夠經過調用list()函數包裝zip()的返回值來恢復zip()函數之前的功能,list()函數會遍歷這個zip()函數返回的迭代器,而後返回結果的列表表示。
  2. 在已經會遍歷序列全部元素的上下文環境裏(好比這裏對join()方法的調用),zip()返回的迭代器可以正常工做。2to3腳本會檢測到這些狀況,不會對你的代碼做出改變。

StandardError異常#

在Python 2裏,StandardError是除了StopIterationGeneratorExitKeyboardInterruptSystemExit以外全部其餘內置異常的基類。在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裏,它包含了表明全部基本數據類型的常量,如dictint。在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。它是一個抽象數據類型,是strunicode類型的超類(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

  1. 使用全局的zip()函數,而非itertools.izip()
  2. 使用map()而非itertools.imap()
  3. itertools.ifilter()變成了filter()
  4. 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
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

  1. 在聲明類的時候聲明metaclass參數,這在Python 2和Python 3裏都有效,它們是同樣的。
  2. 在類的定義裏聲明__metaclass__屬性在Python 2裏有效,可是在Python 3裏再也不有效。
  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纔有真正意義上的布爾類型,因此開發者之前使用10替代。)當代的Python程序員應該鍛鍊他們的大腦以使用這些慣例的現代版。

2to3腳本默認不會爲這些慣例作修復。爲了開啓這個功能,在命令行調用2to3的時候指定-f idioms參數。

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)do_stuff(a_list)
相關文章
相關標籤/搜索