Python 3 的新特性

Python 3 是 Guido van Rossum 功能強大的通用編程語言的最新版本。它雖然打破了與 2.x 版本的向後兼容性,但卻清理了某些語法方面的問題。本文是系列文章中的第一篇,介紹了影響該語言及向後兼容性的各種變化,並且還提供了新特性的幾個例子。

Python 版本 3,也被稱爲 Python 3000 或 Py3K(仿效 Microsoft® Windows® 2000 操作系統而命名的暱稱)是 Guido van Rossum 通用編程語言的最新版本。雖然新版本對該核心語言做了很多改進,但還是打破了與 2.x 版本的向後兼容性。其他一些變化則是人們期待已久的,比如:

   真正的除法 — 例如,1/2 返回的是 .5

  • long 和 int 類型被統一爲一種類型,刪除了後綴 L
  • TrueFalse 和 None 現在都是關鍵字。

本文 — Python 3 系列文章中的第一篇 — 的內容涵蓋了新的 print() 函數、input()、輸入/輸出(I/O)的變化、新的 bytes 數據類型、字符串和字符串格式化的變化以及內置的 dict 類型的變化。本文面向的是那些熟悉 Python 並對新版本的變化很感興趣但又不想費力讀完所有 Python Enhancement Proposal(PEP)的編程人員。(本文後面的 參考資料 部分提供了有關這些 PEP 的鏈接。)

新的 print() 函數

如今,您將需要讓手指習慣於鍵入 print("hello"),而不是原來的 print "hello",這是因爲 print 現在是一個函數,不再是一個語句。我知道,這多少有點痛苦。我認識的每個 Python 程序員 — 一旦安裝了版本 3 並得到 “語法不正確” 錯誤 — 都會鬱悶地大叫。我知道這兩個額外的符號十分討厭;我也知道這將會破壞向後兼容性。但是這種改變還是有好處的。

讓我們考慮這樣的情況,即需要將標準輸出(stdout)重定向到一個日誌。如下的例子會打開文件 log.txt 以便進行追加並將對象指定給 fid。之後,利用 print>> 將一個字符串重定向給文件 fid

>>>fid = open("log.txt", "a")
>>>print>>fid, "log text"

另外一個例子是重定向給標準錯誤(sys.stderr):

>>>print>>sys.stderr, "an error occurred"

上述兩個例子都不錯,但還有更好的解決方案。新的語法只要求給 print() 函數的關鍵字參數 file 傳遞一個值就可以了。比如:

>>>fid = open("log.txt", "a")
>>>print("log.txt", file=fid)

這樣的代碼,語法更爲清晰。另一個好處是通過向 sep 關鍵字參數傳遞一個字符串就能更改分割符(separator),通過向 end 關鍵字參數傳遞另外一個字符串就能更改結束字符串。要更改分割符,可以利用:

>>>print("Foo", "Bar", sep="%")
>>>Foo%Bar

總地來說,新的語法爲:

print([object, ...][, sep=' '][, end='endline_character_here'][, file=redirect_to_here])

其中,方括號([])內的代碼是可選的。默認地,若只調用 print() 自身,結果會追加一個換行符( \n)。

從 raw_input() 到 input()

在 Python 版本 2.x 中,raw_input() 會從標準輸入(sys.stdin)讀取一個輸入並返回一個字符串,且尾部的換行符從末尾移除。下面的這個例子使用 raw_input() 從命令提示符獲取一個字符串,然後將值賦給 quest

>>>quest = raw_input("What is your quest? ")
What is your quest? To seek the holy grail.
>>>quest
'To seek the holy grail.'

與之不同,Python 2.x 中的 input() 函數需要的是一個有效的 Python 表達式,比如 3+5

最初,曾有人建議將 input() 和 raw_input() 從 Python 內置的名稱空間一併刪除,因此就需要進行導入來獲得輸入能力。這從方法上就不對;因爲,簡單鍵入:

>>>quest = input("What is your quest?")

將會變爲:

>>>import sys
>>>print("What is your quest?")
>>>quest = sys.stdin.readline()

對於一個簡單輸入而言,這太過繁瑣,並且對於一個新手,這未免太難理解。往往需要向他們講述模塊 和導入 究竟是怎麼回事、字符串輸出以及句點操作符又是如何工作的(如此麻煩的話,與 Java™ 語言就沒什麼差別了)。所以,在 Python 3 內,將 raw_input() 重命名爲input(),這樣一來,無須導入也能從標準輸入獲得數據了。如果您需要保留版本 2.x 的 input() 功能,可以使用 eval(input()),效果基本相同。

有關 bytes 的簡介

新的數據類型 bytes literal 及 bytes 對象的用途是存儲二進制數據。此對象是 0 到 127 的不可修改的整數序列或純粹的 ASCII 字符。實際上,它是版本 2.5 中 bytearray 對象的不可修改版本。一個 bytes literal 是一個前面冠以 b 的字符串 — 例如,b'byte literal'。對 bytes literal 的計算會生成一個新的 bytes 對象。可以用 bytes() 函數創建一個新的 bytes 對象。bytes 對象的構造函數爲:

bytes([initializer[, encoding]])

例如:

>>>b = (b'\xc3\x9f\x65\x74\x61')
>>>print(b)
b'\xc3\x83\xc2\x9feta'

會創建一個 bytes 對象,但這是多餘的,因爲通過賦值一個 byte literal 就完全可以創建 bytes 對象。(我只是想要說明這麼做是可行的,但是我並不建議您這麼做。)如果您想要使用 iso-8859-1 編碼,可以嘗試下面的做法:

>>>b = bytes('\xc3\x9f\x65\x74\x61', 'iso-8859-1')
>>>print(b)
b'\xc3\x83\xc2\x9feta'

如果初始化器(initializer)是一個字符串,那麼就必須提供一種編碼。如果初始化器是一個 bytes literal,則無須指定編碼類型:請記住,bytes literal 並不是字符串。但是與字符串相似,可以連接多個字節:

>>>b'hello' b' world'
b'hello world'

用 bytes() 方法代表二進制數據以及被編碼的文本。要將 bytes 轉變爲 str, bytes 對象必須要進行解碼(稍後會詳細介紹)。二進制數據用 decode() 方法編碼。例如:

>>>b'\xc3\x9f\x65\x74\x61'.decode()
'ßeta'

也可以從文件中直接讀取二進制數據。請看以下的代碼:

>>>data = open('dat.txt', 'rb').read() 
>>>print(data) # data is a string
>>># content of data.txt printed out here

它的功能是打開文件以便在二進制模式內讀取一個文件對象,並在整個文件內進行讀取。

字符串

Python 具有單一的字符串類型 str,其功能類似於版本 2.x 的 unicode 類型。換言之,所有字符串都是 unicode 字符串。而且 — 對非拉丁文的文本用戶也非常方便 — 非-ASCII 標識符現在也是允許的。例如:

>>>césar = ["author", "consultant"]
>>>print(césar)
['author', 'consultant']

在 Python 之前的版本內,repr() 方法會將 8-位字符串轉變爲 ASCII。例如:

>>>repr('é')
"'\\xc3\\xa9'"

現在,它會返回一個 unicode 字符串:

>>>repr('é')
"'é'"

正如我之前提到的,這個字符串是內置的字符串類型。

字符串對象和字節對象是不兼容的。如果想要得到字節的字符串表示,需要使用它的 decode() 方法。相反,如果想要從該字符串得到 bytes literal 表示,可以使用字符串對象的 encode() 方法。

字符串格式化方面的變化

很多 Python 程序員都感覺用來格式化字符串的這個內置的 % 操作符太有限了,這是因爲:

  • 它是一個二進制的操作符,最多隻能接受兩個參數。
  • 除了格式化字符串參數,所有其他的參數都必須用一個元組(tuple)或是一個字典(dictionary)進行擠壓。

這種格式化多少有些不靈活,所以 Python 3 引入了一種新的進行字符串格式化的方式(版本 3 保留了 % 操作符和 string.Template 模塊)。字符串對象現在均具有一個方法 format(),此方法接受位置參數和關鍵字參數,二者均傳遞到 replacement 字段 。Replacement 字段在字符串內由花括號({})標示。replacement 字段內的元素被簡單稱爲一個字段。以下是一個簡單的例子:

>>>"I love {0}, {1}, and {2}".format("eggs", "bacon", "sausage")
'I love eggs, bacon, and sausage'

字段 {0}{1} 和 {2} 通過位置參數 eggs、 bacon 和 sausage 被傳遞給 format() 方法。如下的例子顯示瞭如何使用 format() 通過關鍵字參數的傳遞來進行格式化:

>>>"I love {a}, {b}, and {c}".format(a="eggs", b="bacon", c="sausage")
'I love eggs, bacon, and sausage'

下面是另外一個綜合了位置參數和關鍵字參數的例子:

>>>"I love {0}, {1}, and {param}".format("eggs", "bacon", param="sausage")
'I love eggs, bacon, and sausage'

請記住,在關鍵字參數之後放置非關鍵字參數是一種語法錯誤。要想轉義花括號,只需使用雙倍的花括號,如下所示:

>>>"{{0}}".format("can't see me")
'{0}'

位置參數 can't see me 沒有被輸出,這是因爲沒有字段可以輸出。請注意這不會產生錯誤。

新的 format() 內置函數可以格式化單個值。比如:

>>>print(format(10.0, "7.3g"))
       10

換言之,g 代表的是 一般格式,它輸出的是寬度固定的值。小數點前的第一個數值指定的是最小寬度,小數點後的數值指定的是精度。format specifier 的完整語法超出了本文的討論範圍,更多信息,可以參見本文的 參考資料 小節。

內置 dict 類型的變化

3.0 內的另一個重大改變是字典內 dict.iterkeys()、 dict.itervalues() 和 dict.iteritems() 方法的刪除。取而代之的是.keys()、 .values() 和 .items(),它們被進行了修補,可以返回輕量的、類似於集的容器對象,而不是鍵和值的列表。這樣的好處是在不進行鍵和條目複製的情況下,就能在其上執行 set 操作。例如:

>>>d = {1:"dead", 2:"parrot"}
>>>print(d.items())
<built-in method items of dict object at 0xb7c2468c>

注意:在 Python 內, 是惟一元素的無序集合。

這裏,我創建了具有兩個鍵和值的一個字典,然後輸出了 d.items() 的值,返回的是一個對象,而不是值的列表。可以像 set 對象那樣測試某個元素的成員資格,比如:

>>>1 in d # test for membership
True

如下是在 dict_values 對象的條目上進行迭代的例子:

>>>for values in d.items():
...     print(values) 
...
dead
parrot

不過,如果您的確想要得到值的列表,可以對所返回的 dict 對象進行強制類型轉換。比如:

>>>keys = list(d.keys())
>>>print(keys)
[1,2]

新的 I/O

元類

Wikipedia 對元類的定義是這樣的,“一個元類 是這樣一個類,其實例也是類。” 在本系列的第 2 部分我會對這個概念進行詳細的介紹。

在深入研究 I/O 的新機制之前,很有必要先來看看抽象基類( abstract base classes,ABC)。更深入的介紹將會在本系列的第 2 部分提供。

ABC 是一些無法被實例化的類。要使用 ABC,子類必須繼承自此 ABC 並且還要覆蓋其抽象方法。如果方法的前綴使用 @abstractmethod 修飾符(decorator),那麼此方法就是一個抽象方法。新的 ABC 框架還提供了 @abstractproperty 修飾符以便定義抽象屬性。可以通過導入標準庫模塊 abc 來訪問這個新框架。清單 1 所示的是一個簡單的例子。

清單 1. 一個簡單的抽象基類
from abc import ABCMeta

class SimpleAbstractClass(metaclass=ABCMeta):
    pass

SimpleAbstractClass.register(list)

assert isinstance([], SimpleAbstractClass)

register() 方法調用接受一個類作爲其參數並會讓此 ABC 成爲所註冊類的子類。這一點可以通過在最後一行上調用 assert 語句進行驗證。清單 2 是使用修飾符的另外一個例子。

清單 2. 使用修飾符的一個抽象基類
from abc import ABCMeta, abstractmethod

class abstract(metaclass=ABCMeta):
    @abstractmethod
    def absMeth(self):
        pass
 
class A(abstract):
    # must implement abstract method
    def absMeth(self):
        return 0

瞭解了 ABC 之後,我們就可以繼續探究新的 I/O 系統了。之前的 Python 發佈版都缺少一些重要但是出色的函數,比如用於類似於流的對象的seek()。 類似於流的對象 是一些具有 read() 和 write() 方法的類似於文件的對象 — 比如,socket 或文件。Python 3 具有很多針對類似於流的對象的 I/O 層 — 一個原始的 I/O 層、一個被緩衝的 I/O 層以及一個文本 I/O 層 — 每層均由其自身的 ABC 及實現定義。

打開一個流還是需要使用內置的 open(fileName) 函數,但是也可以調用 io.open(fileName))。這麼做會返回一個緩衝了的文本文件;read() 和 readline() 會返回字符串(請注意,Python 3 內的所有字符串都是 unicode)。您也可以使用 open(fileName, 'b') 打開一個緩衝了的二進制文件。在這種情況下,read() 會返回字節,但 readline() 則不能用。

此內置 open() 函數的構造函數是:

open(file,mode="r",buffering=None,encoding=None,errors=None,newline=None,closefd=True)

可能的模式有:

  • r
  • w打開供寫入
  • a打開供追加
  • b二進制模式
  • t文本模式
  • +打開一個磁盤文件供更新
  • U通用換行模式

默認的模式是 rt,即打開供讀取的文本模式。

buffering 關鍵字參數的期望值是以下三個整數中的一個以決定緩衝策略:

  • 0關閉緩衝
  • 1行緩衝
  • > 1完全緩衝(默認)

默認的編碼方式獨立於平臺。關閉文件描述符或 closefd 可以是 True 或 False。如果是 False,此文件描述符會在文件關閉後保留。若文件名無法奏效的話,那麼 closefd 必須設爲 True。

open() 返回的對象取決於您所設置的模式。表 1 給出了返回類型。

表 1. 針對不同打開模式的返回類型
模式 返回對象
文本模式 TextIOWrapper
二進制 BufferedReader
寫二進制 BufferedWriter
追加二進制 BufferedWriter
讀/寫模式 BufferedRandom

請注意:文本模式可以是 w、 rwt、 rt 等。

清單 3 中所示的例子打開的是一個緩衝了的二進制流以供讀取。

清單 3. 打開一個緩衝了的二進制流以供讀取
>>>import io
>>>f = io.open("hashlib.pyo", "rb")  # open for reading in binary mode
>>>f                                 # f is a BufferedReader object 
<io.BufferedReader object at 0xb7c2534c>
>>>f.close()                         # close stream

BufferedReader 對象可以訪問很多有用的方法,比如 isatty、 peekraw、 readintoreadline、 readlinesseekseekabletell、 writablewrite 和 writelines。要想查看完整列表,可以在 BufferedReader 對象上運行 dir()

原文地址:http://www.ibm.com/developerworks/cn/linux/l-python3-1/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章