# Python標準ライブラリでデータ保存とシリアライズ:`json`、`pickle`、`csv` データをファイルに保存したりネットワークで送受信したりするには、**シリアライズ(Serialization)**が必要です。 Pythonは、用途に応じて3つの標準ツールを提供しています:**`json`**、**`pickle`**、**`csv`**。 ![データ配送フォーマット業者](/media/editor_temp/6/82b2d285-16b0-473a-b88a-cd9a6f6582f2.png) * **`json`**:人間が読めるテキストフォーマット、言語間の交換に強い * **`pickle`**:Pythonオブジェクトをそのまま保存するバイナリフォーマット、強力だがリスクがある * **`csv`**:表(テーブル)データを最もシンプルに格納するテキストフォーマット、汎用性が高い この記事では「どのデータをどこで使うか」に応じて3つのモジュールを選ぶ感覚を整理します。 --- ## 1. JSON {#sec-c0452ee3f8b9} ### 1.1 概要 {#sec-573894aef250} JSON(JavaScript Object Notation)は**テキストベース**で**言語非依存**です。 設定ファイル、APIのレスポンス/リクエスト、ログなど「他のシステムとやり取りしやすい形」で広く使われます。Pythonでは`json`モジュールで直接扱えます。 ### 1.2 コアAPI(よく使うものだけ) {#sec-7388e1478aff} * `json.dump(obj, fp, ...)` / `json.load(fp, ...)`:ファイルへ保存/読み込み * `json.dumps(obj, ...)` / `json.loads(s, ...)`:文字列へ変換/復元 覚えておくと便利なオプション: * `ensure_ascii=False`:ASCII以外の国際文字(例:ラテン拡張、キリル、CJKなど)をエスケープせずにそのまま保存/出力 * `indent=2`:見やすく整形 * `default=...`:JSONが知らない型を変換する「回避策」 ### 1.3 使用例(基本) {#sec-57d0c0653337} ```python 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の「型制限」を簡単に解決する方法 {#sec-a43d0fe8a199} JSONは基本的に`dict`、`list`、`str`、`int/float`、`bool`、`None`のみ自然に扱えます。 例えば`datetime`はそのまま保存できません。このとき`default`を使うとスッキリです。 `default`には**変換関数(callable)**を渡します。`json`がシリアライズできないオブジェクトに出会うと、そのオブジェクトが関数の引数に入り、関数は`str`/`dict`/`list`など**JSONが理解できる型**に変換して返します。 ```python 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 長所と短所 {#sec-667d6fad184b} **長所** * 言語非依存で交換・共有に強い * テキストなのでデバッグしやすく、Gitなどのバージョン管理にも適している * `pickle`のようにロードだけでコードが実行される構造ではない **短所** * 型表現力が限定的(特に`datetime`、`set`、`Decimal`、バイナリデータなど) * 大量データでは容量・速度面で劣ることがある --- ## 2. Pickle {#sec-7417b1955ccc} ### 2.1 概要 {#sec-1889607ea8cb} `pickle`は**Pythonオブジェクトを「Pythonそのまま」保存する**シリアライズ方式です。結果は人間が読めるテキストではなく**バイナリデータ**(bytes)で、再読み込みするとほぼ同一のオブジェクトに復元されます。 `pickle`が特に有用なケースは次のとおりです。 * **複雑なPythonオブジェクトをそのまま保存**したいとき 例:ユーザー定義クラスのインスタンス、入れ子構造、学習済みモデル/設定オブジェクト(Pythonオブジェクト形式)、計算結果のキャッシュなど * **他言語との交換が不要**なとき `pickle`はPython専用フォーマットなので、共有/交換目的なら通常は`json`の方が適しています。 逆に、次の状況では避ける方が良いです。 * **信頼できない出所のデータを開くとき**(`pickle.load()`は危険) * **他言語/システムとデータをやり取りする**とき(互換性がない) --- ### 2.2 コアAPI(最も使われる4つ) {#sec-a0a97fe87a25} * `pickle.dump(obj, file, protocol=...)`:オブジェクト → ファイル(バイナリ)へ保存 * `pickle.load(file)`:ファイルから読み込み → オブジェクトへ復元 * `pickle.dumps(obj, protocol=...)`:オブジェクト → bytesへ変換 * `pickle.loads(data)`:bytes → オブジェクトへ復元 以下のように「ファイルへ保存/読み込み」が最も一般的です。 ```python 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`とは何か、そしてなぜ気にするのか {#sec-1f8e5565848c} `protocol`は**pickleがデータを保存する「フォーマットバージョン」**です。 プロトコルバージョンによってファイルサイズ/速度/サポート機能が変わります。 * `protocol`を**指定しない**場合:Pythonが「現在の環境で最適なデフォルト」を使用します。 * 通常は**デフォルトのままで十分**です。 * 変更が必要になるケースはほぼ2つです: 1. **非常に古いPythonバージョンと互換**させるために低いプロトコルを強制する場合 2. ファイルサイズ/速度を最適化するために**明示的に最新プロトコル**を固定したい場合 よく使われるパターンは「最新プロトコル」を指定することです。 ```python 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の本当の注意点(重要) {#sec-007b70a5e9ec} `pickle.load()` / `pickle.loads()`は**データ内にある「復元ロジック」を実行**します。 つまり、**信頼できないpickleをロードすると**意図しない動作が発生する可能性があります。 * 安全な選択:**「知らない出所の`.pkl`ファイルは開かない」** * 共有/交換目的なら:**`json`などテキストベースのフォーマットがデフォルト** ### 2.5 長所と短所 {#sec-bbd5a24bec31} **長所** * Pythonオブジェクトを幅広く保存(ほぼ何でも) * 通常`json`より高速で便利(特に複雑なオブジェクト) **短所** * セキュリティリスク(信頼できないデータは絶対にロードしない) * Python専用(他言語と互換性がない) * クラス定義が変わると過去のpickleが壊れる可能性(バージョン互換性の問題) --- ## 3. CSV {#sec-99e590b24e50} ### 3.1 概要 {#sec-1265cc44a461} CSV(Comma-Separated Values)は表形式データを格納する最も単純なフォーマットです。 スプレッドシート、データのエクスポート/インポート、簡易ログダンプなどで頻繁に登場します。 ### 3.2 コアAPI(よく使うものだけ) {#sec-5b2e4c37cce5} * `csv.reader`、`csv.writer`:リストベース * `csv.DictReader`、`csv.DictWriter`:辞書ベース(ほとんどはこちらが便利) ### 3.3 使用例(DictWriter/DictReader) {#sec-524921270351} ```python 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つ {#sec-3db1b7ed8444} 1. **`newline=""`を必ず指定** * 特にWindowsで行末が二重になる問題を防ぐため推奨 2. **型は基本的にすべて文字列** * 読み込むと`"30"`のように文字列で入る。必要な列は手動で変換 3. **区切り文字/引用符/改行がデータに含まれる可能性** * CSVは単純に見えるが「値にカンマが入ると」ルールが発生。`csv`モジュールを使うと安全 ### 3.5 長所と短所 {#sec-6a8ca4891a8e} **長所** * 非常に軽量で汎用的(ほとんどのツールが読み書き可能) * 表データには過剰に単純で扱いやすい **短所** * ネスト構造(リスト内のリスト、辞書の入れ子など)は表現が難しい * 型情報がないため変換コストがかかる --- ## 4. 比較 & 選択ガイド {#sec-6304ec188cc0} | 項目 | JSON | Pickle | CSV | | ------ | --------- | ------------ | -------- | | 言語互換 | 非常に良い | Python専用 | 非常に良い | | 可読性 | 高い | 低い(バイナリ) | 高い | | 型表現力 | 制限的 | 非常に高い | 制限的 | | 安全性 | 比較的安全 | **注意が必要** | 比較的安全 | | データ形式 | 木構造(ネスト) | 「Pythonオブジェクトそのまま」 | テーブル(行/列) | 一文でまとめると: * **他システムとやり取りする** → `json` * **Pythonオブジェクトを丸ごと保存する** → `pickle`(ただしロード元は厳密に) * **行/列形式で出力・読み込み** → `csv` --- ## 5. 同じデータを3種類で保存/復元する {#sec-21f8c2a6d6c9} 以下は同じデータを`json`、`pickle`、`csv`でそれぞれ保存し、再度読み込む例です。 ```python 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. まとめ {#sec-f88db3766ed6} Python標準ライブラリだけでデータ保存とシリアライズは驚くほど広い範囲をカバーします。 * **人も読め、他言語も読める** → `json` * **Pythonオブジェクトをそのまま保管したい** → `pickle` * **表形式で簡単に出力したい** → `csv` この3つのモジュールの中で、**保存したいデータの形状と目的**に合わせて選べば、選択が速くてスッキリします。