python2 與 python3 語法區別

快樂糖果屋

python2 與 python3 語法區別

python2 與 python3 語法區別

概述#

原稿地址:使用 2to3 將代碼移植到 Python 3

幾乎所有的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)
  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)

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裏的長整型。由於已經不存在兩種類型的整數,所以就沒有必要使用特殊的語法去區別他們。

進一步閱讀: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,標準庫裏的一些模塊已經被重命名了。還有一些相互關聯的模塊也被組合或者重新組織,以使得這種關聯更有邏輯性。

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 SimpleHTTPServer
import 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 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
  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 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)現在在單獨的一個包裏,即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 whichdb
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 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
  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 mbcharsetproberimport MultiByteCharSetProber from .mbcharsetproberimport MultiByteCharsetProber
  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 = 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__()
  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 mymodule
except ImportError, e
    pass
try:
    import mymodule
except ImportError as e:
    pass
try:
    import mymodule
except (RuntimeError, ImportError), e
    pass
try:
    import mymodule
except (RuntimeError, ImportError) as e:
    pass
try:
    import mymodule
except ImportError:
    pass
no change
try:
    import mymodule
except:
    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_typesys.exc_valuesys.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)

python2 與 python3 語法區別

概述#

原稿地址:使用 2to3 將代碼移植到 Python 3

幾乎所有的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)
  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)

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裏的長整型。由於已經不存在兩種類型的整數,所以就沒有必要使用特殊的語法去區別他們。

進一步閱讀: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,標準庫裏的一些模塊已經被重命名了。還有一些相互關聯的模塊也被組合或者重新組織,以使得這種關聯更有邏輯性。

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 SimpleHTTPServer
import 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 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
  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 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)現在在單獨的一個包裏,即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 whichdb
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 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
  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 mbcharsetproberimport MultiByteCharSetProber from .mbcharsetproberimport MultiByteCharsetProber
  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 = 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__()
  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 mymodule
except ImportError, e
    pass
try:
    import mymodule
except ImportError as e:
    pass
try:
    import mymodule
except (RuntimeError, ImportError), e
    pass
try:
    import mymodule
except (RuntimeError, ImportError) as e:
    pass
try:
    import mymodule
except ImportError:
    pass
no change
try:
    import mymodule
except:
    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_typesys.exc_valuesys.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)

 轉自:https://www.cnblogs.com/meng-wei-zhi/articles/8194801.htmlPython

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章