Python 標準函式庫中的資料儲存與序列化:jsonpicklecsv

若要將資料寫入檔案或透過網路傳輸,必須先進行序列化(Serialization)。Python 為此提供了三種特性各異的標準工具:jsonpicklecsv

資料傳輸格式供應商

  • json:可讀文本格式,跨語言交換友好
  • pickle:將 Python 物件直接存成二進位格式,功能強大但存在風險
  • csv:最簡單的表格資料文本格式,通用性高

本文將根據「資料用途」,整理並說明這三個模組的選擇時機與考量。


1. JSON



1.1 概述

JSON(JavaScript Object Notation)是基於文字語言獨立的格式。設定檔、API 回應/請求、日誌等「適合與其他系統交換」的資料常用此格式。Python 直接使用 json 模組處理。

1.2 核心 API(常用)

  • json.dump(obj, fp, ...) / json.load(fp, ...):寫入/讀取檔案
  • json.dumps(obj, ...) / json.loads(s, ...):轉成字串/從字串還原

常用選項:

  • ensure_ascii=False:不將非 ASCII 字元轉成 \uXXXX,直接保留原文
  • indent=2:美化輸出
  • default=...:提供一個「轉換」函式,處理 JSON 無法序列化的型別,將其轉換為可序列化的形式。

1.3 使用範例(基本)

import json

data = {
    "name": "Alice",
    "age": 30,
    "skills": ["Python", "Data Science"]
}

with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)

print(loaded)

1.4 方便處理 JSON 的「型別限制」

JSON 只天然支援 dictliststrint/floatboolNone。例如 datetime 需要額外處理。可透過 default 參數傳入函式,將不支援型別轉成 JSON 可接受的型別。

import json
from datetime import datetime, timezone

payload = {"created_at": datetime.now(timezone.utc)}

def to_jsonable(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError(f"Not JSON serializable: {type(obj)!r}")

s = json.dumps(payload, default=to_jsonable, ensure_ascii=False)
print(s)

1.5 優缺點

優點

  • 語言獨立,易於交換與分享
  • 文字格式,易於除錯與版本控制
  • 不像 pickle 那樣在載入時執行程式碼

缺點

  • 型別表達有限(如 datetimesetDecimal、二進位資料)
  • 大量資料時,容量與速度可能不如二進位格式

2. Pickle

2.1 概述

pickle 是一種旨在「完整保留 Python 物件原始狀態」進行序列化的方式。輸出為二進位資料(bytes),再次載入時可得到近乎相同的物件。

pickle 特別適用於:

  • 想保留自訂類別實例、巢狀資料結構、已訓練模型/設定物件等
  • 不需要與其他語言交換

相反,若需跨語言或信任度低的來源,應避免使用。


2.2 核心 API(最常用 4 個)

  • pickle.dump(obj, file, protocol=...):寫入檔案
  • pickle.load(file):讀取檔案
  • pickle.dumps(obj, protocol=...):轉成 bytes
  • pickle.loads(data):從 bytes 讀取
import pickle

data = {"a": [1, 2, 3], "b": ("x", "y")}

with open("data.pkl", "wb") as f:
    pickle.dump(data, f)

with open("data.pkl", "rb") as f:
    loaded = pickle.load(f)

print(loaded)

2.3 protocol 是什麼?為何要注意?

protocol 是 pickle 存檔時使用的「格式版本」。不同版本會影響檔案大小、速度與支援功能。

  • 未指定時,Python 會選擇「最適合目前環境」的預設值
  • 只在以下兩種情況下需要手動設定: 1. 與非常舊的 Python 版本保持相容 2. 想明確使用最新、最快的格式

常見做法是使用 pickle.HIGHEST_PROTOCOL(目前最高的協定號)。

import pickle

with open("data.pkl", "wb") as f:
    pickle.dump({"x": 1}, f, protocol=pickle.HIGHEST_PROTOCOL)

2.4 Pickle 的主要注意事項(重要)

pickle.load() / pickle.loads() 會執行資料中包含的「反序列化邏輯」。因此,載入不可信的 pickle 可能導致惡意程式執行

  • 安全做法:不要載入未知來源的 .pkl
  • 若需交換,建議改用 json 等文字格式

2.5 優缺點

優點

  • 能存任何 Python 物件(幾乎無限制)
  • 通常比 json 更快、更方便(尤其是複雜物件)

缺點

  • 安全風險(不可信來源不可載入)
  • 只適用於 Python,無法跨語言
  • 物件定義變更可能破壞舊 pickle(版本相容問題)

3. CSV



3.1 概述

CSV(Comma-Separated Values)是最簡單的表格資料格式。常見於試算表、資料匯出/匯入、簡易日誌等。

3.2 核心 API(常用)

  • csv.readercsv.writer:基於列表
  • csv.DictReadercsv.DictWriter:基於字典(大多使用此方式)

3.3 使用範例(DictWriter/DictReader)

import csv

data = [
    {"name": "Alice", "age": 30, "city": "Seoul"},
    {"name": "Bob",   "age": 25, "city": "Busan"},
]

with open("people.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
    writer.writeheader()
    writer.writerows(data)

with open("people.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    loaded = list(reader)

print(loaded)

3.4 CSV 的三點注意事項

  1. 一定要加 newline="":避免 Windows 產生雙行
  2. 所有資料皆為字串:讀取後需自行轉型
  3. 分隔符/引號/換行可能出現在資料中:使用 csv 模組可自動處理

3.5 優缺點

優點

  • 輕量、通用(大多工具可讀寫)
  • 對表格資料極簡易處理

缺點

  • 無法表達巢狀結構(如列表內列表、字典巢狀)
  • 缺少型別資訊,需手動轉換

4. 比較 & 選擇指引

項目 JSON Pickle CSV
語言相容 非常好 只適用 Python 非常好
可讀性 低(二進位)
型別表達 限制 非常高 限制
安全性 相對安全 需注意 相對安全
資料結構 樹狀(巢狀) 原始 Python 物件 表格(行/列)

一句話總結:

  • 跨系統交換json
  • 欲完整保留 Python 物件狀態pickle(但來源必須可信)
  • 表格輸出/輸入csv

5. 同一資料用三種方式儲存/還原

以下示範同一資料分別以 jsonpicklecsv 儲存並讀取。

import json, pickle, csv

data = [
    {"id": 1, "name": "Alice", "score": 95.5},
    {"id": 2, "name": "Bob",   "score": 88.0},
]

# 1) JSON
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

with open("data.json", "r", encoding="utf-8") as f:
    json_loaded = json.load(f)

# 2) Pickle
with open("data.pkl", "wb") as f:
    pickle.dump(data, f)

with open("data.pkl", "rb") as f:
    pickle_loaded = pickle.load(f)

# 3) CSV
with open("data.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["id", "name", "score"])
    writer.writeheader()
    writer.writerows(data)

with open("data.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    csv_loaded = [
        {"id": int(row["id"]), "name": row["name"], "score": float(row["score"])}
        for row in reader
    ]

print(json_loaded)
print(pickle_loaded)
print(csv_loaded)

重點:

  • CSV 讀取後需手動轉型
  • Pickle 方便但須注意來源安全

6. 結語

僅靠 Python 標準函式庫,就能滿足資料儲存與序列化的廣泛需求。

  • 需要跨語言或人可讀json
  • 想保留 Python 物件完整pickle
  • 想簡單輸出表格csv

根據「資料形態」與「目的」選擇合適模組,即可快速、乾淨地完成儲存與還原。