# Chapter A 使用`2to3`將代碼移植到Python 3
> " Life is pleasant. Death is peaceful. It’s the transition that’s troublesome. "
> — Isaac Asimov (attributed)
## 概述
幾乎所有的Python 2程序都需要一些修改才能正常地運行在Python 3的環境下。為了簡化這個轉換過程,Python 3自帶了一個叫做`2to3`的實用腳本(Utility Script),這個腳本會將你的Python 2程序源文件作為輸入,然后自動將其轉換到Python 3的形式。[案例研究:將`chardet`移植到Python 3(porting chardet to Python 3)](case-study-porting-chardet-to-python-3.html#running2to3)描述了如何運行這個腳本,然后展示了一些它不能自動修復的情況。這篇附錄描述了它_能夠_自動修復的內容。
## `print`語句
在Python 2里,`print`是一個語句。無論你想輸出什么,只要將它們放在[`print`](your-first-python-program.html#divingin)關鍵字后邊就可以。在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)](strings.html#divingin)。
| 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字符串](strings.html#divingin),所以`str()`函數即可完成所有的功能。(`unicode()`函數在Python 3里不再存在了。)
| Notes | Python 2 | Python 3 |
| --- | --- | --- |
| | `unicode(anything)` | `str(anything)` |
## `long`?長整型
Python 2有為非浮點數準備的`int`和`long`類型。`int`類型的最大值不能超過[`sys.maxint`](#renames),而且這個最大值是平臺相關的。可以通過在數字的末尾附上一個`L`來定義長整型,顯然,它比`int`類型表示的數字范圍更大。在Python 3里,[只有一種整數類型`int`](native-datatypes.html#numbers),大多數情況下,它很像Python 2里的長整型。由于已經不存在兩種類型的整數,所以就沒有必要使用特殊的語法去區別他們。
[進一步閱讀:PEP 237:統一長整型和整型](http://www.python.org/dev/peps/pep-0237/)。
| 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`運算符](native-datatypes.html#mixed-value-dictionaries)。
| 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)` |
1. 最簡單的形式。
2. 運算符`or`的優先級高于運算符`in`,所以這里不需要添加括號。
3. 另一方面,出于同樣的原因?—?`or`的優先級大于`in`,這里需要添加括號。(注意:這里的代碼與前面那行完全不同。Python會先解釋`x or y`,得到結果`x`(如果`x`[在布爾上下文里的值是真](native-datatypes.html#booleans))或者`y`。然后Python檢查這個結果是不是`a_dictionary`的一個鍵。)
4. 運算符`in`的優先級大于運算符`+`,所以代碼里的這種形式從技術上說不需要括號,但是`2to3`還是添加了。
5. 這種形式一定需要括號,因為`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_ |
1. 使用`list()`函數將`keys()`的返回值轉換為一個靜態列表,出于安全方面的考量,`2to3`可能會報錯。這樣的代碼是有效的,但是對于使用視圖來說,它的效率低一些。你應該檢查轉換后的代碼,看看是否一定需要列表,也許視圖也能完成同樣的工作。
2. 這是另外一種視圖(關于`items()`方法的)到列表的轉換。`2to3`對`values()`方法返回值的轉換也是一樣的。
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`文件](http://www.robotstxt.org/)。
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`。如果你需要其中某個特定的變體,比如GNU DBM,你可以導入`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 |
| --- | --- | --- |
| ① | \[1\] | `import io` |
| ② | \[2\] | `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\]:
```
try:
import cStringIO as StringIO
except ImportError:
import StringIO
```
\[2\]:
```
try:
import cPickle as pickle
except ImportError:
import pickle
```
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 mbcharsetprober import MultiByteCharSetProber` | `from .mbcharsetprober import MultiByteCharsetProber` |
1. 當你需要從包的其他地方導入整個模塊,使用新的`from . import`語法。這里的句號(.)即表示當前文件(`universaldetector.py`)和你想要導入文件(`constants.py`)之間的相對路徑。在這個樣例中,這兩個文件在同一個目錄里,所以使用了單個句號。你也可以從父目錄(`from .. import anothermodule`)或者子目錄里導入。
2. 為了將一個特定的類或者函數從其他模塊里直接導入到你的模塊的名字空間里,在需要導入的模塊名前加上相對路徑,并且去掉最后一個斜線(slash)。在這個例子中,`mbcharsetprober.py`與`universaldetector.py`在同一個目錄里,所以相對路徑名就是一個句號。你也可以從父目錄(from .. import anothermodule)或者子目錄里導入。
## 迭代器方法`next()`
在Python 2里,迭代器有一個`next()`方法,用來返回序列里的下一項。在Python 3里這同樣成立,但是現在有了一個新的全局的函數[`next()`](generators.html#generators),它使用一個迭代器作為參數。
| Notes | Python 2 | Python 3 |
| --- | --- | --- |
| ① | `anIterator.next()` | `next(anIterator)` |
| ② | `a_function_that_returns_an_iterator().next()` | `next(a_function_that_returns_an_iterator())` |
| ③ | \[1\] | \[2\] |
| ④ | \[3\] | _no change_ |
| ⑤ | \[4\] | \[5\] |
\[1\]:
```
class A:
def next(self):
pass
```
\[2\]:
```
class A:
def __next__(self):
pass
```
\[3\]:
```
class A:
def next(self, x, y):
pass
```
\[4\]:
```
next = 42
for an_iterator in a_sequence_of_iterators:
an_iterator.next()
```
\[5\]:
```
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 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_ |
1. 最簡單的情況下,`2to3`會用一個`list()`函數來包裝`filter()`,`list()`函數會遍歷它的參數然后返回一個列表。
2. 然而,如果`filter()`調用已經被`list()`包裹,`2to3`不會再做處理,因為這種情況下`filter()`的返回值是否是一個迭代器是無關緊要的。
3. 為了處理`filter(None, ...)`這種特殊的語法,`2to3`會將這種調用從語法上等價地轉換為列表解析。
4. 由于`for`循環會遍歷整個序列,所以沒有必要再做修改。
5. 與上面相同,不需要做修改,因為列表解析會遍歷整個序列,即使`filter()`返回一個迭代器,它仍能像以前的`filter()`返回列表那樣正常工作。
## 全局函數`map()`
跟[`filter()`](#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_ |
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`語句](#print)在Python 3里變成了一個函數一樣,`exec`語句也是這樣的。`exec()`函數使用一個包含任意Python代碼的字符串作為參數,然后就像執行語句或者表達式一樣執行它。`exec()`跟[`eval()`](advanced-iterators.html#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`語句](#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,[捕獲異常](your-first-python-program.html#exceptions)的語法有些許變化。
| Notes | Python 2 | Python 3 |
| --- | --- | --- |
| ① | \[1\] | \[2\] |
| ② | \[3\] | \[4\] |
| ③ | \[5\] | _no change_ |
| ④ | \[6\] | _no change_ |
\[1\]:
```
try:
import mymodule
except ImportError, e
pass
```
\[2\]:
```
try:
import mymodule
except ImportError as e:
pass
```
\[3\]:
```
try:
import mymodule
except (RuntimeError, ImportError), e
pass
```
\[4\]:
```
try:
import mymodule
except (RuntimeError, ImportError) as e:
pass
```
\[5\]:
```
try:
import mymodule
except ImportError:
pass
```
\[6\]:
```
try:
import mymodule
except:
pass
```
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里,[拋出自定義異常](your-first-python-program.html#exceptions)的語法有細微的變化。
| 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 i in xrange(10)]` | `[i for i in 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)而不再是列表的字典類方法](#dict)一樣,這同樣適用于`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 |
| --- | --- | --- |
| ① | \[1\] | \[2\] |
| ② | \[3\] | _no change_ |
\[1\]:
```
class A:
def __nonzero__(self):
pass
```
\[2\]:
```
class A:
def __bool__(self):
pass
```
\[3\]:
```
class A:
def __nonzero__(self, x, y):
pass
```
1. 當在布爾上下文使用一個類對象時,Python 3會調用`__bool__()`,而非`__nonzero__()`。
2. 然而,如果你有定義了一個使用兩個參數的`__nonzero__()`方法,`2to3`腳本會假設你定義的這個方法有其他用處,因此不會對代碼做修改。
## 八進制類型
在Python 2和Python 3之間,定義八進制(octal)數的語法有輕微的改變。
| Notes | Python 2 | Python 3 |
| --- | --- | --- |
| | `x = 0755` | `x = 0o755` |
## `sys.maxint`
由于[長整型和整型被整合在一起](#long)了,`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)` |
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`是除了`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`模塊里仍然還有許多其他的有用的函數](advanced-iterators.html#more-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 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類型的)](strings.html#divingin),所以你只需要`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 |
| --- | --- | --- |
| ① | \[1\] | _unchanged_ |
| ② | \[2\] | \[3\] |
| ③ | \[4\] | \[5\] |
\[1\]:
```
class C(metaclass=PapayaMeta):
pass
```
\[2\]:
```
class Whip:
__metaclass__ = PapayaMeta
```
\[3\]:
```
class Whip(metaclass=PapayaMeta):
pass
```
\[4\]:
```
class C(Whipper, Beater):
__metaclass__ = PapayaMeta
```
\[5\]:
```
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代碼風格的官方指導手冊](http://www.python.org/dev/peps/pep-0008/)?—?細致到能使人痛苦?—?都是一些你不太可能關心的在各種各樣的細節上的挑剔。鑒于`2to3`為轉換代碼提供了一個這么好的條件,腳本的作者們添加了一些可選的特性以使你的代碼更具可讀性。
### `set()`字面值(literal)(顯式的)
在Python 2城,定義一個字面值集合(literal set)的唯一方法就是調用`set(a_sequence)`。在Python 3里這仍然有效,但是使用新的標注記號(literal notation):大括號({})是一種更清晰的方法。這種方法除了空集以外都有效,因為字典也用大括號標記,所以[`{}`表示一個空的字典,而不是一個空集](native-datatypes.html#emptyset)。
> ?`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 |
| --- | --- | --- |
| | \[1\] | \[2\] |
| | `type(x) == T` | `isinstance(x, T)` |
| | `type(x) is T` | `isinstance(x, T)` |
| | \[3\] | \[4\] |
\[1\]:
```
while 1:
do_stuff()
```
\[2\]:
```
while True:
do_stuff()
```
\[3\]:
```
a_list = list(a_sequence)
a_list.sort()
do_stuff(a_list)
```
\[4\]:
```
a_list = sorted(a_sequence)
do_stuff(a_list)
```
- 版權信息
- Chapter -1 《深入 Python 3》中有何新內容
- Chapter 0 安裝 Python
- Chapter 1 你的第一個 Python 程序
- Chapter 2 內置數據類型
- Chapter 3 解析
- Chapter 4 字符串
- Chapter 5 正則表達式
- Chapter 6 閉合 與 生成器
- Chapter 7 類 & 迭代器
- Chapter 8 高級迭代器
- Chapter 9 單元測試
- Chapter 10 重構
- Chapter 11 文件
- Chapter 12 XML
- Chapter 13 序列化Python對象
- Chapter 14 HTTP Web 服務
- Chapter 15 案例研究:將chardet移植到Python 3
- Chapter 16 打包 Python 類庫
- Chapter A 使用2to3將代碼移植到Python 3
- Chapter B 特殊方法名稱
- Chapter C 接下來閱讀什么?