Python標準ライブラリでデータ保存とシリアライズ:jsonpicklecsv

データをファイルに保存したりネットワークで送受信したりするには、シリアライズ(Serialization)が必要です。 Pythonは、用途に応じて3つの標準ツールを提供しています:jsonpicklecsv

データ配送フォーマット業者

  • json:人間が読めるテキストフォーマット、言語間の交換に強い
  • pickle:Pythonオブジェクトをそのまま保存するバイナリフォーマット、強力だがリスクがある
  • csv:表(テーブル)データを最もシンプルに格納するテキストフォーマット、汎用性が高い

この記事では「どのデータをどこで使うか」に応じて3つのモジュールを選ぶ感覚を整理します。


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以外の国際文字(例:ラテン拡張、キリル、CJKなど)をエスケープせずにそのまま保存/出力
  • 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を使うとスッキリです。

defaultには変換関数(callable)を渡します。jsonがシリアライズできないオブジェクトに出会うと、そのオブジェクトが関数の引数に入り、関数はstr/dict/listなど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 長所と短所

長所

  • 言語非依存で交換・共有に強い
  • テキストなのでデバッグしやすく、Gitなどのバージョン管理にも適している
  • pickleのようにロードだけでコードが実行される構造ではない

短所

  • 型表現力が限定的(特にdatetimesetDecimal、バイナリデータなど)
  • 大量データでは容量・速度面で劣ることがある

2. Pickle

2.1 概要

picklePythonオブジェクトを「Pythonそのまま」保存するシリアライズ方式です。結果は人間が読めるテキストではなくバイナリデータ(bytes)で、再読み込みするとほぼ同一のオブジェクトに復元されます。

pickleが特に有用なケースは次のとおりです。

  • 複雑なPythonオブジェクトをそのまま保存したいとき 例:ユーザー定義クラスのインスタンス、入れ子構造、学習済みモデル/設定オブジェクト(Pythonオブジェクト形式)、計算結果のキャッシュなど
  • 他言語との交換が不要なとき pickleはPython専用フォーマットなので、共有/交換目的なら通常はjsonの方が適しています。

逆に、次の状況では避ける方が良いです。

  • 信頼できない出所のデータを開くときpickle.load()は危険)
  • 他言語/システムとデータをやり取りするとき(互換性がない)

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とは何か、そしてなぜ気にするのか

protocolpickleがデータを保存する「フォーマットバージョン」です。 プロトコルバージョンによってファイルサイズ/速度/サポート機能が変わります。

  • protocol指定しない場合:Pythonが「現在の環境で最適なデフォルト」を使用します。
  • 通常はデフォルトのままで十分です。
  • 変更が必要になるケースはほぼ2つです: 1. 非常に古いPythonバージョンと互換させるために低いプロトコルを強制する場合 2. ファイルサイズ/速度を最適化するために明示的に最新プロトコルを固定したい場合

よく使われるパターンは「最新プロトコル」を指定することです。

import pickle

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

まとめ:最初はprotocolを気にしなくてよい。必要になったらpickle.HIGHEST_PROTOCOLを思い出せばOKです。


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の注意点ポイント3つ

  1. newline=""を必ず指定 * 特にWindowsで行末が二重になる問題を防ぐため推奨
  2. 型は基本的にすべて文字列 * 読み込むと"30"のように文字列で入る。必要な列は手動で変換
  3. 区切り文字/引用符/改行がデータに含まれる可能性 * CSVは単純に見えるが「値にカンマが入ると」ルールが発生。csvモジュールを使うと安全

3.5 長所と短所

長所

  • 非常に軽量で汎用的(ほとんどのツールが読み書き可能)
  • 表データには過剰に単純で扱いやすい

短所

  • ネスト構造(リスト内のリスト、辞書の入れ子など)は表現が難しい
  • 型情報がないため変換コストがかかる

4. 比較 & 選択ガイド

項目 JSON Pickle CSV
言語互換 非常に良い Python専用 非常に良い
可読性 高い 低い(バイナリ) 高い
型表現力 制限的 非常に高い 制限的
安全性 比較的安全 注意が必要 比較的安全
データ形式 木構造(ネスト) 「Pythonオブジェクトそのまま」 テーブル(行/列)

一文でまとめると:

  • 他システムとやり取りするjson
  • Pythonオブジェクトを丸ごと保存するpickle(ただしロード元は厳密に)
  • 行/列形式で出力・読み込みcsv

5. 同じデータを3種類で保存/復元する

以下は同じデータを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

この3つのモジュールの中で、保存したいデータの形状と目的に合わせて選べば、選択が速くてスッキリします。