Python 標準程式庫
大型的標準模組庫
- 可執行許多實用的工作,並且被分開,以免讓核心的語言過於肥大
- 編寫 Python 程式時,先檢查是否已經有標準模組可以代勞,通常是值得的
Python 官方模組文件:http://docs.python.org/3/library
- Python 教學課程:https://docs.python.org/3.3/tutorial/stdlib.html
- Doug Hellmann 實用的指南
- 網站:Python Module of the Week https://pymotw.com/2/contents.html https://pymotw.com/3/
- 書:The Python Standard Library by Example (Addison-Wesley Professional) https://doughellmann.com/blog/the-python-3-standard-library-by-example/
接下來的章節會討論 Web、系統、資料庫等專用的標準模組。這一節,會討論一些標準的通用模組。
處理遺漏的鍵【使用 setdefault() 與 defaultdict()】
- 不存在的鍵存取字典 → 產生例外
- 字典 get() 函式 → 回傳一個預設值,避免例外發生
- setdefault()函式 → 就像 get(),但是如果鍵遺漏的話,也會指派一個項目給字典
setdefault()
鍵沒有在字典裡面 → 使用新值
>>> 字典名稱 = {'鍵1': 值1, '鍵2': 值2}
>>> print(字典名稱)
{'鍵1': 值1, '鍵2': 值2}
>>> 變數1 = 字典名稱.setdefault('鍵3': 值3)
>>> 變數1 # 鍵沒有在字典裡面 → 使用新值
值3
>>> 字典名稱
{'鍵1': 值1, '鍵2': 值2, '鍵3': 值3}
>>> periodic_table = {'Hydrogen': 1, 'Helium': 2}
>>> print(periodic_table)
{'Hydrogen': 1, 'Helium': 2}
>>> carbon = periodic_table.setdefault('Carbon', 12)
>>> carbon
12
>>> periodic_table
{'Hydrogen': 1, 'Helium': 2, 'Carbon': 12}
指派「不同的預設值」給「一個已經存在的鍵」 → 回傳原始值,不會改變任何東西
>>> 變數2 = 字典名稱.setdefault('鍵2': 值4) # 指派「不同的預設值」給「一個已經存在的鍵」
>>> 變數2
鍵2 # 回傳原始值,不會改變任何東西
>>> 字典名稱
{'鍵1': 值1, '鍵2': 值2, '鍵3': 值3}
>>> helium = periodic_table.setdefault('Helium', 947) # 指派「不同的預設值」給「一個已經存在的鍵」
>>> helium
2 # 回傳原始值,不會改變任何東西
>>> periodic_table
{'Hydrogen': 1, 'Helium': 2, 'Carbon': 12}
defaultdict()
字典被建立時,為任何新鍵指定預設值(它的引數是個函式)
【範例】傳遞函式 → 當它被呼叫時,會使用 int() → 回傳整數0
# 傳遞函式 → 當它被呼叫時,會使用 int() → 回傳整數0
>>> from collections import defaultdict
>>> 字典名稱 = defaultdict(int)
# 現在,任何遺漏的值都會是一個整數(int),其值為0
>>> 字典名稱['鍵1'] = 值1
>>> 字典名稱['鍵5'] # 任何遺漏的值都會是一個整數(int),其值為0
0
>>> 字典名稱
defaultdict(<class 'int'>, {'鍵1': '值1', '鍵5': 0})
>>> from collections import defaultdict # defaultdict()很類似,但它會在字典被建立時,「為任何新鍵指定預設值」
>>> periodic_table = defaultdict(int) # defaultdict(),它的引數是個函式
# 傳遞函式 int,當它被呼叫時,會使用 int(),並回傳整數 0
>>> periodic_table['Hydrogen'] = 1
>>> periodic_table['Lead'] # 現在,任何遺漏的值都會是一個整數(int),其值為0
0
>>> periodic_table
defaultdict(<class 'int'>, {'Hydrogen': 1, 'Lead': 0})
被傳入 defaultdict() 的引數是「一個函式」→ 回傳將要被指派給「遺漏的鍵的值」
【範例】必要時,執行 no_idea() → 以回傳一個值
>>> from collections import defaultdict
>>> def no_idea():
return '文字?' # 回傳將要被指派給「遺漏的鍵的值」
>>> 變數3 = defaultdict(no_idea) # 被傳入 defaultdict() 的引數是「一個函式」
>>> 變數3['鍵6'] = '值6'
>>> 變數3['鍵7'] = '值7'
>>> 變數3['鍵6']
'值6'
>>> 變數3['鍵7']
'值7'
>>> 變數3['鍵8'] # 回傳「遺漏的鍵的值」
'文字'
>>> from collections import defaultdict
>>> def no_idea():
return 'Huh?'
>>> bestiary = defaultdict(no_idea) # 被傳入 defaultdict() 的引數是「一個函式」→ 回傳將要被指派給「遺漏的鍵的值」
>>> bestiary['A'] = 'Abominable Snowman'
>>> bestiary['B'] = 'Basilisk'
>>> bestiary['A']
'Abominable Snowman'
>>> bestiary['B']
'Basilisk'
>>> bestiary['C'] # 必要時,執行 no_idea() → 以回傳一個值
'Huh?'
使用函式 int()、list() 或 dict() → 回傳這些類型的「預設空白值」
- int() → 回傳 0
- list() → 回傳 空串列([])
- dict() → 回傳 空字典({})
- 省略引數 → 「新鍵的初始值」會被設為「None」
使用 lambda → 在呼叫式中「定義預設的函式」
>>> 變數3 = defaultdict(lambda: '文字')
>>> 變數3['鍵9']
'文字'
>>> bestiary = defaultdict(lambda: 'Huh?') # 使用 lambda → 在呼叫式中「定義預設的函式」
>>> bestiary['E']
'Huh?'
製作計數器【使用int, for...in 】
>>> from collections import defaultdict
>>> 變數4 = defaultdict(int)
>>> for 引數1 in ['字串1', '字串1', '字串2', '字串1']:
變數4[引數1] += 1
>>> for 引數1, 引數2 in 變數4.items():
print(引數1, 引數2)
字串2 1
字串1 3
>> from collections import defaultdict
>>> food_counter = defaultdict(int)
>>> for food in ['spam', 'spam', 'eggs', 'spam']:
food_counter[food] += 1
>>> for food, count in food_counter.items():
print(food, count)
spam 3
eggs 1
一般字典,而不是 defaultdict,遞增字典元素 → 發出例外(因為尚未被初始化)
>>> 字典名稱 = {}
>>> for 引數1 in ['字串1', '字串1', '字串2', '字串1']:
if not 引數 in 字典名稱:
字典名稱[引數] = 0
字典名稱[引數] += 1
>>> for 引數1, 引數2 in 字典名稱.items():
print(引數1, 引數2)
字串1 3
字串2 1
# 如果 food_counter 是一個一般的字典,而不是 defaultdict
# 每當試著遞增字典元素 food_counter[food]時,Python 都會發出例外,因為它尚未被初始化
# 必須額外做些事情,如下所示
>>> dict_counter = {}
>>> for food in ['spam', 'spam', 'eggs', 'spam']:
if not food in dict_counter:
dict_counter[food] = 0
dict_counter[food] += 1
>>> for food, count in dict_counter.items():
print(food, count)
spam 3
eggs 1
計算項目數量【使用 counter()】
標準程式庫有一個計數器(除了之前的工作之外,它也能做其他的工作)
>>> from collections import Counter
>>> 串列名稱1 = ['字串1', '字串1', '字串2', '字串1']
>>> 串列名稱1_counter = Counter(串列名稱)
>>> 串列名稱1_counter
Counter({'字串1':3, '字串3': 1})
>>> from collections import Counter
>>> breakfast = ['spam', 'spam', 'eggs', 'spam']
>>> breakfast_counter = Counter(breakfast)
>>> breakfast_counter
Counter({'spam': 3, 'eggs': 1})
降冪回傳所有元素,或當指定一個數量時,回傳最前面的 count 個元素【使用 most_common()】
>>> 串列名稱1_counter.most_common()
[('字串1', 3), ('字串2', 1)]
>>> 串列名稱1_counter.most_common(1)
[('字串1', 3)]
>>> breakfast_counter.most_common()
[('spam', 3), ('eggs', 1)]
>>> breakfast_counter.most_common(1)
[('spam', 3)]
結合計數器使用
>>> 串列名稱1.counter
>>> Counter({'字串1': 3, '字串2': 1})
>>> breakfast_counter
Counter({'spam': 3, 'eggs': 1})
製作一個新的串列 + 一個計數器
>>> 串列名稱2 = ['字串2', '字串2', '字串3']
>>> 串列名稱2_counter = Counter(串列名稱)
>>> 串列名稱2_counter
Counter({'字串2': 2, '字串3': 1})
>>> lunch = ['eggs', 'eggs', 'bacon']
>>> lunch_counter = Counter(lunch)
>>> lunch_counter
Counter({'eggs': 2, 'bacon': 1})
結合兩個計數器【使用加法 +】
>>> 串列名稱1_counter + 串列名稱2_counter
Counter({'字串1': 3, '字串2': 3, '字串3': 1})
>>> breakfast_counter + lunch_counter
Counter({'spam': 3, 'eggs': 3, 'bacon': 1})
將另一個計數器減去另一個【使用 -】
【範例一】什麼食物早餐有,中餐沒有?
>>> 串列名稱1_counter - 串列名稱2_counter
Counter({'字串1': 3})
>>> breakfast_counter - lunch_counter
Counter({'spam': 3})
【範例二】什麼食物午餐有,早餐沒有?
>>> 串列名稱2_counter - 串列名稱1_counter
Counter({'字串2': 1, '字串3': 1})
>>> lunch_counter - breakfast_counter
Counter({'eggs': 1, 'bacon': 1})
使用交集運算子 & 來取得共同的項目
交集會選出數量較少的共同元素
>>> 串列名稱1_counter & 串列名稱2_counter # 交集會選出數量較少的共同元素
Counter({'字串2': 1})
>>> breakfast_counter & lunch_counter
Counter({'eggs': 1})
聯集運算子 | 來取得所有項目
兩邊都有的項目,與加法不同的是,聯集不會加總它們的數量,而是選取數量較多的那一個項目
>>> 串列名稱1_counter | 串列名稱2_counter
Counter({'字串1': 3, '字串2': 2, '字串3': 1})
>>> breakfast_counter | lunch_counter
Counter({'spam': 3, 'eggs': 2, 'bacon': 1})
排序【使用鍵與 OrderedDict()】
- 之前許多範例程式都說明,在字典中的「鍵的順序是無法預測的」→ 可能按照 a、b、c 的順序加入鍵,但 keys() 可能會回傳 c、a、b
>>> 字典名稱 = {
'鍵1': '值1',
'鍵2': '值2',
'鍵3': '值3',
}
>>> for stooge in 字典名稱:
print(stooge)
鍵2
鍵3
鍵1
會記得加入鍵的順序,並以「迭代器」按照同樣的順序回傳它們【使用 OrderedDict()】
【範例】用一系列的 (鍵, 值) tuple 來建立一個 OrderedDict
>>> from collections import OrderedDict
>>> tuple名稱 = OrderedDict([
('鍵1', '值1'),
('鍵2', '值2'),
('鍵3', '值3'),
])
>>> for stooge in tuple名稱:
print(stooge)
鍵1
鍵2
鍵3
>>> quotes = {
'Moe': 'A wise guy, huh?',
'Larry': 'Ow!',
'Curly': 'Nyuk nyuk!',
}
>>> for stooge in quotes:
print(stooge)
Moe
Larry
Curly
堆疊 + 序列 == deque
- deque(唸成 deck),是一種雙頭的序列 → 它同時具有堆疊與序列的功能
- 目標:
- 想要從序列的任何一端「加入」或「刪除項目」時
- 從一個單字的兩端檢查到它的中間 → 看看是否是個回文(palindrome)
- 使用方式:
- 將 deque 最左邊的項目移除、並回傳它 → 使用 popleft() 函式
- 最右邊的項目移除、並回傳它 → 使用 pop()
- 它們會同時從兩端開始,往中間移動
- 只要兩端的字元相符,它就會繼續 pop,直到到達最中間為止
>>> def palindrome(word):
from collections import deque
dq = deque(word)
while len(dq) > 1:
if dq.popleft() != dq.pop():
return False
return True
>>> palindrome('a')
True
>>> palindrome('racecar')
True
>>> palindrome('')
True
>>> palindrome('radar')
True
>>> palindrome('halibut')
False
快速的回文檢查器
- 目的:為了簡單地說明 deque
- 簡單做法:將「字串」與「它自己的相反」比較 → 可以用 slice 來反轉字串(Python 沒有字串的 reverse()函式)
>>> def another_palindrome(word):
return word == word[::-1] # 將「字串」與「它自己的相反」比較 → 可以用 slice 來反轉字串
>>> another_palindrome('radar')
True
>>> another_palindrome('halibut')
False
迭代程式結構【使用 itertools】
- itertools(https://docs.python.org/3/library/itertools.html) 裡面有一些特殊的「迭代函式」
- 在 for...in 迴圈中呼叫它們時 → 會一次回傳一個項目,並且記得每次呼叫之間的狀態
- itertools 模組還有許多函式,特別是有些排列與組合可以為你節省很多時間
逐一經過它的引數,就像它們是可迭代的單一項目【使用 chain()】
>>> import itertools
>>> for item in itertools.chain([數字1, 數字2], ['文字1', '文字2']):
print(item)
數字1
數字2
文字1
文字2
>>> import itertools
>>> for item in itertools.chain([1, 2], ['a', 'b']):
print(item)
1
2
a
b
無限迭代器,會循環經過它的引數【使用 cycle()】
>>> import itertools
>>> for item in itertools.cycle([數字1, 數字2]):
print(item)
數字1
數字2
數字1
數字2
.
.
.
>>> import itertools
>>> for item in itertools.cycle([1, 2]):
print(item)
1
2
1
2
.
.
.
【accumulate() 使用方法一】計算累計的值 → 在預設情況下,它會計算總和
>>> import itertools
>>> for item in itertools.accumlate([數字1, 數字2, 數字3, 數字4]):
print(item)
數字1
數字1 + 數字2
數字1 + 數字2 + 數字3
數字1 + 數字2 + 數字3 + 數字4
.
>>> import itertools
>>> for item in itertools.accumulate([1, 2, 3, 4]):
print(item)
1
3
6
10
【accumulate() 使用方法二】第二個引數提供一個函式 → 它會被用來取代加法
- 在 accumulate()第二個引數提供一個函式 → 它會被用來取代加法
>>> import itertools
>>> def multiply(引數1, 引數2):
return 引數1 * 引數2
>>> for item in itertools.accumulate([數字1, 數字2, 數字3, 數字4], multiply):
print(item)
數字1
數字1 * 數字2
數字1 * 數字2 * 數字3
數字1 * 數字2 * 數字3 * 數字4
>>> for item in itertools.accumulate([1, 2, 3, 4], multiply):
print(item)
1
2
6
24
印出好看的結果【使用 pprint()】
- 我們的範例都使用 print()(或是在互動式解譯器中,只使用變數名稱)來印出東西
- 有時結果會難閱讀,所以需要更棒的輸出程式 → 例如 pprint()
- print() → 只會將東西傾印出來
- pprint() → 會試著排列元素,讓你更容易閱讀
>>> from pprint import pprint
>>> tuple名稱 = OrderedDict([
('鍵1': '值1'),
('鍵2': '值2'),
('鍵3': '值3'),
])
# print() → 只會將東西傾印出來
>>> print(tuple名稱)
OrderedDict([('鍵1': '值1'), ('鍵2': '值2'), ('鍵3': '值3')])
>>> from pprint import pprint
>>> from collections import OrderedDict
>>> quotes = OrderedDict([
('Moe', 'A wise guy, huh?'),
('Larry', 'Ow!'),
('Curly', 'Nyuk nyuk!')
])
>>> print(quotes)
OrderedDict([('Moe', 'A wise guy, huh?'), ('Larry', 'Ow!'), ('Curly', 'Nyuk nyuk!')])
pprint() → 會試著排列元素,讓你更容易閱讀
pprint(tuple名稱) {'鍵1': '值1', '鍵2': '值2' '鍵3': '值3'} ```
>>> from pprint import pprint
>>> from collections import OrderedDict
>>> quotes = OrderedDict([
('Moe', 'A wise guy, huh?'),
('Larry', 'Ow!'),
('Curly', 'Nyuk nyuk!')
])
>>> print(quotes)
OrderedDict([('Moe', 'A wise guy, huh?'), ('Larry', 'Ow!'), ('Curly', 'Nyuk nyuk!')])
>>> pprint(quotes)
OrderedDict([('Moe', 'A wise guy, huh?'),
('Larry', 'Ow!'),
('Curly', 'Nyuk nyuk!')])
更多電池:取得其他的 Python 程式
- 有時標準的程式庫沒有你要的東西,或沒有採用非常正確的工作方式
- 全世界都有開放原始碼、第三方的 Python 軟體供你使用
資源
- 以下是優秀的資源
- PyPi(https://pypi.python.org/pypi)
- github(https://github.com/python)
- readthedocs(https://readthedocs.org/)
- 可以在 activestate 找到許多小型的範例程式(http://code.activestate.com/recipes/langs/python/)
關於本書
- 本書幾乎所有的 Python 程式都使用你的電腦所安裝的標準 Python,包括所有內建與標準程式庫
- 有些地方會使用外部的套件:在第一章提過 requests,並且會在本書第240頁的 "標準程式庫之外:Requests" 進一步說明
- 本書附錄D 也會展示如何安裝第三方 Python 軟體,以及許多其他的開發細節