10. 資料永續與交換
• 學習目標
– 使用 pickle 與 shelve
– 認識 DB-API 2.0
– 使用 sqlite3 模組
– 處理 CSV、JSON、XML
2
使用 pickle 模組
• 如果要序列化 Python 物件,可以使用內建
的 pickle 模組
• 將一個Python 物件轉換為 bytes,這稱為
Pickling,相反的操作則稱之為 unpickling,
會將 bytes 轉換為 Python 物件
3
• 若想將物件轉換為 bytes,可以使用
dumps() 函式
• 若想將一個代表物件的 bytes 轉換為物件,
可以使用 loads() 函式
4
• 可以 pickling 與unpickling 的型態包括內
建型態、使用者自定義的頂層函式、類別
等
• 如果無法進行 pickling 或 unpickling,就
會引發 PicklingError 或
UnpicklingError
5
• 保存在檔案,從檔案讀取
6
7
• pickling 時實際採用的模式,是 Python 的
專屬格式
• pickle 的保證是能向後相容未來的新版本
• 可以使用 pickle.HIGHEST_PROTOCOL
來得知目前可用的最新格式版本為哪一個
• pickle.DEFAULT_PROTOCOL 則是
pickle 模組的預設版本
• 如果必要指定格式版本,
• 可以在使用 dumps()、dump()、loads()
或 load() 時,指定其 protocol 參數
8
使用 shelve 模組
• shelve 物件行為上像是字典的物件
• 鍵的部份必須是字串,值的部份可以是
pickle 模組可處理的 Python 物件
• 它直接與一個檔案關聯,因此使用上就像
個簡單的資料庫介面
9
10
11
12
13
認識 DB-API 2.0
• DB-API 2.0 由PEP 249 規範,所有的資料
庫介面都應該符合這個規範
• 以便撰寫程式時能有一致的方式,撰寫出
來的程式也便於跨資料庫執行
• Connection 基本上要具備以下的方法:
14
• Cursor 物件基本上必須具備以下方法:
15
使用 sqlite3 模組
• Python 中內建了 SQLite 資料庫,這是個
用 C 語言撰寫的輕量級資料庫
• 資料庫本身的資料可以儲存在一個檔案中,
或者是記憶體之中,後者對於資料庫應用
程式的測試非常的方便
• 若想使用 SQLite 作為資料庫,並撰寫
Python 程式與資料庫進行操作,可以使用
sqlite3 模組
16
建立資料庫與連線
• 可以傳給 connect() 一個 ':memory:'
字串,這樣會在記憶體中建立一個資料庫
17
建立表格與新增資料
18
• Connection 物件實作了情境管理器,可
以搭配 with 陳述使用
• 在 with 區塊的動作完成之後,會自動
commit() 與 close(),若發生例外,則
會自動 rollback()
19
• 若要新增一筆資料,也是使用 Cursor 的
execute()方法
20
查詢資料
• 先用 Cursor 的 execute()執行查詢語句
• fetchone() 可以取得結果集合中的一筆
資料
• fetchall()取得結果集合中的全部資料
• fetchmany() 指定要從結果集合中取得幾
筆資料
21
更新與刪除資料
22
參數化 SQL 語句
• 直接使用 + 來串接字串以組成 SQL,容易
引發 SQL Injection 的安全問題
23
• 使用 f-strings、字串的 format(),或者
是舊式的 % 進行格式化,也會有同樣問題
24
• Cursor 的 execute() 方法本身可以將
SQL 語句參數化
• 有兩種參數化的方式:使用問號(?)或具
名佔位符號
25
• 如果你有多筆 SQL 必須執行,雖然可以使
用 for in 自行處理:
26
• 用 Cursor 的 executemany() 會更方便:
27
簡介交易
• 交易的四個基本:
– 要求原子性(Atomicity)
– 一致性(Consistency)
– 隔離行為(Isolation behavior)
– 持續性(Durability)
• 依英文字母首字簡稱為 ACID
28
• 除了一些會隱含地提交之情況,sqlite3
模組的預設實作,並不會自動提交
• 必須自行呼叫 Connection 的 commit()
來進行提交
• 如果交易過程因為發生錯誤或其他情況,
必須撤回交易時,可以呼叫 Connection
的 rollback() 撤回操作
29
• 一個基於例外發生時必須撤消交易的示範:
30
• 在隔離性方面,SQLite 資料庫在更新資料
的相關操作時,預設會鎖定資料庫直到該
次交易完成
• 多個連線時就會造成等待的狀況
• sqlite3 模組的 connect() 函式有個
timeout 可指定等待多久,若逾時就引發
例外,預設是 5.0,也就是是 5 秒
31
• sqlite3 模組的 Connection 物件有個
isolation_level 屬性,可用來設定或
得知目前的隔離性設定
• 預設是'',實際上在 SQLite 資料庫就會產
生 BEGIN 陳述
• 如果 isolation_level 被設置為 None,
表示不做任何的隔離性,也就成為自動提
交,每次 SQL 更新相關操作時,就不用自
行呼叫 Connection 的 commit()方 法
32
• 不設隔離性,在多個連線存取資料庫的情
況下,就會引發資料不一致的問題
– 更新遺失(Lost update)
– 髒讀(Dirty read)
– 無法重複的讀取(Unrepeatable read)
– 幻讀(Phantom read)
33
更新遺失
34
髒讀
35
無法重複的讀取
36
幻讀
• 同一交易期間,讀取到的資料筆數不一致。
例如交易 A 第一次讀取得到五筆資料,此
時交易 B 新增了一筆資料,導致交易 B 再
次讀取得到六筆資料。
37
• 由於各家資料對於交易的支援程度並不相
同,實際上該採用與如何進行設定也就有
所差異
• 就 sqlite3 模組的實作來說,
Connection 物件的 isolation_level
還可以設定 SQLite 資料庫支援的隔離層級
'DEFERRED'、'IMMEDIATE' 或
'EXCLUSIVE'
38
CSV
• 全名為 Comma Separated Values,是通
用在試算表、資料庫間的資料交換格式
• Python 提供了 csv 模組,可隱藏 CSV 的
讀寫細節,讓開發人員輕鬆處理 CSV 格式
39
40
• 若想將先前下載的 CSV 檔案轉存為 UTF-8
的話
41
• 可以使用 csv 的 DictReader()、
DictWriter(),將 CSV 以 dict 的方式
處理
42
• 使用 fieldnames 自行指定欄位名稱:
43
• 有一些 dict,想要寫出為CSV:
44
45
46
JSON
• 全名 JavaScript Object Notation,為
JavaScript 物件實字
• 可以在〈Introducing JSON〉找到詳細的
JSON 格式說明,以及各語言中可處理
JSON 的程式庫
47
• 在 Python 中可以使用 dict 與 list 等來
模仿:
48
• 在 JSON 的物件格式之中:
– 名稱必須用 "" 雙引號包括。
– 值可以是 “” 雙引號包括的字串,或者是數字、
true、false、null、JavaScript 陣列(相
當於Python 的 list)或子 JSON 格式
49
• 數字、true、false、null、使用 "" 包
括的字串等,都是合法的 JSON 格式
• Python 內建了 json 模組,API 的使用上
類似 pickle
• 內建型態轉為 JSON 格式的過程稱為編碼
(Encoding)
• 將 JSON 格式轉為 Python 內建型態之過程
稱為解碼(Decoding)
50
51
• 將 Python 內建型態編碼為 JSON 格式,可
以使用 json.dumps()
52
• indent 參數可指定數字,這會為JSON 格
式加上指定的空白數量進行縮排:
53
• seperators 預設是 (', ', ': '),如
果指定為(',', ':'),就不會有空白了
– 像是在資料進行網路傳輸時,若能省掉不必要
的空白,就可省去不必要的流量開銷
54
• 如果呼叫 json.dump() 時指定了非內建
型態,預設是會引發 TypeError:
55
• 可以指定一個轉換函式給 default 參數,
轉換函式必須傳回 Python 內建型態,以進
行 JSON 編碼:
56
• 若要將物件編碼為 JSON 並寫至檔案:
• 要將 JSON 格式解碼為內建型態物件:
57
• 可以在使用 json.loads() 時,指定一個
函式給 object_hook:
58
• 若要從檔案中讀取 JSON 並解碼:
59
XML
• 在處理XML 時,Python 提供了幾個模組:
– xml.dom
– xml.sax
– xml.etree
60
• Python 建議 xml.etree.ElementTree
• 相對於 DOM 來說,ElementTree 更為簡
單而快速
• 相對於 SAX 來說,也有 iterparse() 可
以使用,可以在讀取 XML 文件的過程中即
時進行處理
61
62
• 取得 XML 中全部的標籤名稱:
63
• 可以使用 fromstring() 來剖析 XML 字
串,這會直接傳回一個 Element 實例,代
表著XML 字串的根節點
64
65
• 可以指定 XPath 表示式來取得想要的標籤:
66
• 想要直接取得 XML 字串的 bytes 資料,
可以使用 tostring():
67
• 可以使用 append() 來附加一個元素,使
用 insert() 來插入元素,使用 remove()
可以移除元素,使用 set() 設定元素屬性
68
69
• 使用 iterparse() 可以針對標籤的
'start'、'end'、'start-ns'、
'end-ns' 事件發生時,進行相對應處理
70

10. 資料永續與交換