Skip to content

fluent python

Fluent Python 讀書筆記(一)

  • Python

特殊方法

  • 資料模型對 Python 來說是一種「框架」,這個框架(可以想成 Python 的解譯器 Interpreter)會呼叫特定的方法(指 method),這個特定的方法就是 Dunder Method
  • 當你實作了 __getitem__,就代表類別實例可以是可迭代(iterable),且支援了 indexing、slicing、函式庫如 random.choice
  • 若可迭代物件沒有實作 __contains__,在 in 運算子下的預設的行為是循序掃描
  • len() 對於內建型態(如 list、str、bytearray),解譯器並非呼叫 __len__,而是回到底層的 C 結構查詢,因為速度快很多 —— 這也說明了為什麼是 len(collection) 而不是 collection.len,這是一種「在內建物件效率與語言一致性之間取得平衡」
  • for 語句私下是呼叫 iter() 再呼叫 __iter__()
  • 一般而言,你的程式不該自己呼叫特殊方法,而是透過內建方法(如 len、iter、str)
  • 交互式終端與除錯程式會對運算式的結果呼叫 repr()
  • __repr__ 回傳的字串必須精確,盡可能匹配原始碼,好表示「物件如何建立的」以利再次重建
  • 沒有自訂的 __str__,解譯器會呼叫 __repr__ 提供回饋
  • 反向運算子 (reverse operator) 應用在交換律的後備機制如 a * bb * a
  • 擴增賦值運算子 (augmented assignment operator) 意在結合中輟運算 (infix) 與賦值行為,如 a <<= b
  • 「讓使用者使用核心開發人員所使用的工具」,我的理解是,將底層 API 開放給上層使用
  • 「特殊方法其實是魔術方法的相反」,魔術功能通常意指「你不能在自己定義的物件中模擬這些功能」

序列

  • 牢記可變 VS. 不可變、容器序列 VS. 一般序列的不同
  • List comprehension 只做一件事:建構新序列
  • tuple 可以當成「不可變序列」來用,也可當成沒有欄位名稱的「紀錄」(有 unpacking 的優勢)
  • 變數名 _ 翻譯成「啞變數」(使用底線有缺點,會被當成 gettext 的函數別名)
  • 當賦值的對象是切片時,賦值物件必須是 iterable,如 l[2:5] = [20, 30]
  • 序列的 +* 一定都是建立新物件
  • += (原地算法)的特殊方法是 __iadd__, i 代表 in-place,如果 __iadd__沒有被實作的話,Python 會退而呼叫 __add__,可以解讀成「是否真的是原地算法,取決於運算哪種物件」—— 同一個概念可以套用到 *=
  • 檢視 Python 的 bytecode: dis.dis('<your code here>')
  • 重要的 Python API 慣例:當函式或方法就地改變物件(沒有建立新的物件)時,必須回傳 None,簡單的範例是 list.sort(),反例是 sorted(list)
  • 另一種快速的數值儲存方式,是使用 pickle 模組來做物件序列化,使用 pickle.dump 來儲存一個陣列的浮點數幾乎和 array.tofile 一樣快
  • 內建的 memoryview 類別是 Python 的一個廣義 NumPy 陣列結構,可以用它在資料結構間共用記憶體(PIL、SQLite、NumPy 陣列等),而不需要先做複製
  • 移除 deque 中間的項目並不是那麼快,它其實最適合在兩端進行操作
  • queue 函式庫實作的佇列如 Queue、LifoQueue 用來在執行緒之間進行安全通訊
  • Python 的 sort 是使用 Timesort,根據資料的排序狀況來決定採用 MergeSort 或是 InsertionSort

Read More »Fluent Python 讀書筆記(一)

Fluent Python Notes: An array of sequences

  • Python

私人筆記,有錯誤煩請指正

Fluent Python Github


序列(Sequence)

可分成容器序列、一般序列;或分成可變及不可變。容器序列保存物件的參考,可以是任何型態;一般序列實際儲存項目的值,但只能保存數字、字元或位元組

容器「有些物件裡面有其他物件的參考,這些物件稱為容器」

collections.abc


List Comprehension(listcomp)

如果你不是只想建構串列,就不該使用listcomp,如果listcomp的長度太長,請考慮用for迴圈。Python 2.x 中listcomp中的變數會影響到外部環境的變數

Generator Expression(genexp)

串列以外的序列類型應該使用genexp,可節省記憶體空間(透過for迴圈一次產生一個項目)


Tuple可充分扮演紀錄的角色,原因是他的拆解機制(Unpacking)


slice跟range排除最後一個項目的原因

  • 容易看出或計算長度,range(start, stop)或my_list[start:stop]的長度都是stop – start
  • 區分序列成多個部份而不會重疊,my_list[:x]與my_list[x:]


建構巢狀串列


重要的Python API慣例

當函式或方法就地改變物件時,必須回傳None,來讓呼叫方知道物件本身已被改變,而且沒有創建新的物件,e.g. list.sort、random.shuffle。這樣做有一個缺點,無法層疊這些方法的呼叫式(Fluent Interface 流式接口);反之,會回傳新的物件的例子如sorted、所有str的方法


待補充:bisect、memorview, numpy.ndarray, collections.deque


拿list來裝混合型態的物件並不實用,因為list的某些操作可能會無法使用,請用tuple,因為相較之下這種作法自然很多(tuple每個項目其實都代表是個欄位)


list.sort與sorted的排序演算法是用Timesort,會根據資料的排序狀況來決定用插入排序還是合併排序

Fluent Python Notes: Data Model

  • Python

私人筆記,有錯誤煩請指正

Fluent Python Github


遵循Steve Holden的做法,在唸出Magic Functions的時候用dunder取代underscore, 如__getitem__唸作”dunder-getitem”


善用namedtuple來建構裡面只有一堆屬性,沒有自訂方法的簡單類別,如資料庫的紀錄一般

註:

  1. nametuple是類別工廠,回傳一個tuple的子類別
  2. 呼叫屬性asdict回傳OrderedDict物件(3.6後版本)
  3. 屬性是immutable(tuple),要更改可以re-create或呼叫_replace
  4. 透過__doc__設定docstring
  5. 透過__default__prototype._replace來設定預設值


實作__getitem__讓物件變成可迭代物(iterable)


某個集合可以透過實作__contains__來定義in運算子要如何掃描集合。


關於特殊方法,它們是要讓Python編譯器呼叫的,而不是你(私下呼叫);使用者程式經常呼叫的特殊方法只有__init__,目的是呼叫你自己寫的__init__;如果你要呼叫特殊方法,呼叫相關的內建函式會比較好(例如len、iter、str等),這些函式不僅會呼叫對應的特殊方法,通常還會提供其他服務,也比較快


__repr__回傳的字串必須精確,而且如果可以的話,必須盡可能匹配原始碼,以重新建立被表示的物件;__str__是讓print函式私下使用的,回傳給終端使用者觀看的格式;如果沒有自訂的__str__可用,Python會呼叫__repr__來提供回饋

https://stackoverflow.com/a/2626364/8100647


注意這裡的方法回傳新的Vector實例。為中綴(infix)運算子的預期行為:為了建立新的物件,並不接觸它們的運算元


len不會被當成方法來呼叫,因為它身為Python資料模型的一部分,會受到特殊對待,如同abs。但是拜特殊方法__len__所賜,你的自訂物件也可以使用len,這是一種在內建物件效率與語言一致性之間取得的平衡