Fluent Python 筆記(二):序列基礎

目錄:

Fluent Python 筆記(一):數據模型

Fluent Python 筆記(二):序列基礎

Fluent Python 筆記(三):高效操作序列

Fluent Python 筆記(四):字典和集合

Fluent Python 筆記(五):把函數當做對象

本篇開始總結 Python 基本的數據結構,大部分情況下,這些數據結構就已經夠用了,不需要重複造輪子。首先是序列及其相關的操作。

序列(Sequence)

標準庫中提供了很多序列類型,都是C實現的,效率很高。

  • 容器序列(Container sequences):

list, tuple, collections.deque ...

容器序列持有的是所包含對象的引用,可以是任意類型。

  • 平坦序列(Flat sequences):

str, bytes, bytearray, memoryview, array.array

平坦序列物理上存儲每個條目對應的內存空間的值,而不是作為不同的對象。

因此,平坦序列更加緊湊,但是他們只能保存原始的數值如字元,位元組和數字。

根據可不可變分類:

可變的序列:

list, bytearray, array.array, collections.deque, memoryview

不可變序列:

tuple, str, bytes

如何快速生成一個序列?

  • 列表推導(List Comprehensions)
  • 生成器表達式(Generator Expressions)

強烈推薦列表推導,用它實現的代碼簡潔,易讀,執行效率高,堪稱完美。

首先舉一個例子:

取出一個字元串列表中所有小寫的字元串,並組成列表

>>> words = [The, quick, BROWN, Fox, jumped, OVER, the, Lazy, DOG]n>>> [word for word in words if word.islower()]n[quick, jumped, the]n

列表推導格式以 [] 為標誌,內容分為三部分 [A B C],A,B,C分別代表一個表達式,其中C可以省略。

對於上面的例子:

  • A:word

  • B:for word in words
  • C:if word.islower()

首先看B,B的格式一般為 for x in xxx,表示從一個序列中逐個選取元素,也就是常用的 for in 結構。可以有多個for in,比如 for x in xxx for y in yyy。使用多個for的時候,就會生成所有組合。例如列舉出不同顏色尺寸的T恤組合 :

>>> colors = [black, white]n>>> sizes = [S, M, L]n>>> tshirts = [(color, size) for color in colors for size in sizes]n>>> tshirtsn[(black, S), (black, M), (black, L), (white, S),n(white, M), (white, L)]n

然後看C,C提供一個判斷條件,格式一般為 if xxx,其中可以用到B所給出的x,用於選取符合條件的條目。可以省略,也就意味著使用B生成的所有條目。

最後看A,A格式隨意,可以使用B中給出的x(或者y),當然也可以不使用。

>>> [word[0] for word in words if word.islower()]n[q, j, t]n>>> [3 for word in words if word.islower()]n[3, 3, 3]n

列表推導時一般控制在一行以內,如果只有多行才能實現,那說明邏輯太複雜了,考慮別用列表推導了,展開吧。

生成器表達式和列表推導唯一的不同是用 () 包圍而不是 [],如果不需要一次性生成整個列表,那麼用生成器表達式更好。如下:

>>> (word for word in words if word.islower())n<generator object <genexpr> at 0x02F00288>n

這樣只是構造了一個生成器對象,每個元素會在需要用到的時候才進行構造,可以原封不動地用於 for in 結構,需要變成列表的時候也可以隨時調用 list() 函數轉化為列表。

元組(Tuple)

元組的主要用途:

  • 作為不可變的列表
  • 作為沒有欄位名稱的記錄

元組解包:

>>> lax_coordinates = (33.9425, -118.408056)n>>> latitude, longitude = lax_coordinates # tuple unpackingn>>> latituden33.9425n>>> longituden-118.408056n

作為函數參數就地展開,在前面加*就可以了:

>>> divmod(20, 8)n(2, 4)n>>> t = (20, 8)n>>> divmod(*t)n(2, 4)n

具名元組(Named Tuples):

因為元組作為記錄比較好用,因此出現了 namedtuple,在 collections 模塊中。

使用 namedtuple 創建的實例消耗的內存和普通元組相同,因為欄位的名字是存儲在類中的。他們使用的內存比普通的類要少,因為它們不用在每個實例的 __dict__ 中存儲屬性。

使用示例:

>>> from collections import namedtuplen>>> Point = namedtuple(Point, x y)n>>> p = Point(3, 4)n>>> pnPoint(x=3, y=4)n>>> p.xn3n>>> p[1]n4n

namedtuple 有幾個有用的屬性和方法:

  • _fields 類屬性,保存所有欄位名稱

>>> Point._fieldsn(x, y)n

  • _make(iterable) 類方法,使用已經存在的序列或者 iterable 來創建 namedtuple

>>> point_tuple = (3, 4)n>>> p = Point._make(point_tuple)n>>> pnPoint(x=3, y=4)n

  • _asdict() 實例方法,返回一個 OrderedDict,映射名稱和對應的值。

>>> p._asdict()nOrderedDict([(x, 3), (y, 4)])n

總結:

Python 中序列的用法是大同小異的,基本上掌握了一個,其他的都差不多,剩下的就是可不可變,存儲的是值還是引用需要注意下。

使用列表推導來構建序列能夠讓代碼變得非常簡潔,容易理解,推薦多嘗試使用。

元組用來作為數據記錄比較好用,namedtuple 使得元組在保持低內存消耗的情況下更加容易調試。

(下一篇介紹序列的通用操作,包括切片,操作符相關,以及一些能夠提高效率的函數)

推薦閱讀:

2017 夢醒時分
Pyinstaller打包後運行,程序一閃而過,怎麼解決?
敢來挑戰嗎——你能想到多少種創建Python對象的方法?
給妹子講python--15迭代環境

TAG:Python | Python教程 |