使用類別與物件 V.S. 模組的時機
以下是決定何時該將程式放入類別或模組的準則
- 當需要許多實例,且它們有類似的行為(方法),但內部的狀態不同(屬性)時,物件是最實用的選擇
- 類別可繼承,模組不行
- 如果某個東西只需要一個,模組可能是最好的選擇
- 無論程式參考某個 Python 模組幾次,它都只會載入一個複本
- 給 Java 與 C++ 程式員:如果對 Erich Gamma 寫的 Design Patterns: Elements of Reusable Object-Oriented Software 這本書很熟的話,可以將 Python 模組當成一個單例(singleton)
- 如果有許多變數,它們裡面有許多值,而且這些值可以當成引數傳給多個函式,將它們定義成類別可能比較好
- 例如,或許會用一個字典,裡面有代表彩色圖像的 size 與 color 等鍵
- 可以在程式中為每一個圖像建立不同的字典,並將它們當成引數傳給 scale() 或 transform() 等函式
- 當加入鍵與函式時,可能會很亂 → 比較一致的作法是:定義一個 Image 類別,並在裡面加入 size 或 color 屬性,及 scale() 與 transform() 方法
- 於是,就可以將「彩色圖像的資料」與「方法」定義在同一個地方
- 使用最簡單的問題解決方式
- 字典、串列與 tuple 都比模組更簡單、更小,且更快
- 模組通常比類別簡單
Guido 的建議
-- Guido van Rossum(https://plus.google.com/u/0/115212051037621986145/posts/HajXHPGN752)
- 避免過度設計資料結構
- Tuple 比物件好(試試具名 tuple)
- 簡單的欄位比 getter/setter 函式好 → 內建的資料類型是你的好朋友
- 儘量使用數字、字串、tuple、串列、集合、字典
- 也試試使用集合、程式庫,特別是 deque
Some patterns for fast Python. Know any others?
Avoid overengineering datastructures. Tuples are better than objects (try namedtuple too though). Prefer simple fields over getter/setter functions.
Built-in datatypes are your friends. Use more numbers, strings, tuples, lists, sets, dicts. Also check out the collections library, esp. deque.
Be suspicious of function/method calls; creating a stack frame is expensive.
Don't write Java (or C++, or Javascript, ...) in Python.
Are you sure it's too slow? Profile before optimizing!
The universal speed-up is rewriting small bits of code in C. Do this only when all else fails.
具名 Tuple
關於「具名 Tuple」
- 因為 Guido 剛才提到它,本書還沒談到,所以這裡很適合討論 具名 tuple(named tuple)
- 具名 tuple 是一種 tuple 的子類別,其中,可以用名稱(使用 .name),以及位置(使用[ offset ])來存取值
將之前範例的「Duck 類別」轉換成「具名 tuple」
- 使用 bill 與 tail 來作為簡單的字串屬性
- 在呼叫 namedtuple 時會使用兩個引數:
- 名稱
- 欄位名稱字串,以空格分開
【範例】具名 Tuple
# Python 並不會自動提供具名 tuple,所以在使用它們之前,必須先載入一個模組 → 在以下範例的第一行做這件事
>>> from collections import namedtuple
>>> Duck = namedtuple('Duck', 'bill tail')
>>> duck = Duck('wide orange', 'long')
>>> duck
Duck(bill='wide orange', tail='long')
>>> duck.bill
'wide orange'
>>> duck.tail
'long'
# 可以用字典來製作具名 tuple
>>> parts = {'bill': 'wide orange', 'tail': 'long'}
>>> duck2 = Duck(**parts)
>>> duck2
Duck(bill='wide orange', tail='long')
# 看一下程式中的 **parts。這是一個關鍵字引數 → 它會取用部份的字典鍵與值,並將它們當成引數傳給 Duck()
# 它的效果相當於:
>>> duck2 = Duck(bill = 'wide orange', tail = 'long')
# 具名 tuple 是不可變的,但可以更換一或多個欄位,並回傳另一個命名 tuple
>>> duck3 = duck2._replace(tail='magnificent', bill='crushing')
>>> duck3
Duck(tail='magnificent', bill='crushing')
# 可以將 duck 定義為字典
>>> duck_dict = {'bill': 'wide orange', 'tail': 'long'}
>>> duck_dict
{'bill': 'wide orange', 'tail': 'long'}
# 可以將欄位加入字典
>>> duck_dict['color'] = 'green'
>>> duck_dict
{'color': 'green', 'bill': 'wide orange', 'tail': 'long'}
# 而不是加到具名 tuple
>>> duck.color = 'green'
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
duck.color = 'green'
AttributeError: 'Duck' object has no attribute 'color'
總之,以下是具名 tuple 的優點
- 它的外觀與行為都像個不可變物件
- 它比物件更節省空間與時間
- 可以用「句點標記法」取代「字典型式的方括號」來存取屬性
- 可以將它當成字典鍵來使用