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迭代環境