Python pickle.dumps()・loads()の使い方|bytesへの変換とdump/load・json.dumpsとの違い、よくあるエラー
pickle.dumps()はPythonのオブジェクトをbytes(バイト列)に変換する関数で、pickle.loads()はそのbytesを元のオブジェクトに戻す関数です。ファイルに書くdump()/load()と違い、結果をメモリ上のbytesとして受け取れるので、DBへの保存や通信・キャッシュに使えます。
この記事では、dumps()とloads()に絞って、戻り値の正体、dump()/load()やjson.dumps()との違い、bytesを文字列にしたいときの対処、DBに保存する例、loads()でよく出るエラーを、実際に動かした結果つきで解説します。pickle全体の概要は「Python pickleとは?オブジェクトを保存・復元する方法を解説」、ファイルへの保存は「Python pickle.dump()とload()とは?オブジェクトをファイルに保存する方法」で扱っています。
コードはPython 3.12.15で実行しました。Python 3.14で既定のプロトコルが4から5に変わったため、3.14以降では出力されるbytesの先頭や長さが少し変わります(公式ドキュメント、2026年10月18日確認)。
pickle.dumps()とpickle.loads()の基本
公式ドキュメントでの定義は次のとおりです(2026年10月18日確認)。
pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None):objをpickle化したものを、ファイルに書く代わりにbytesオブジェクトとして返すpickle.loads(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None):pickle化されたdataから元のオブジェクトを組み立てて返す。dataはbytes-likeオブジェクトでなければならない。プロトコルは自動で判別されるので指定は不要
import pickle
data = {"name": "田中", "age": 30, "tags": ["python", "pickle"]}
b = pickle.dumps(data)
print(type(b), len(b))
print(b[:12])
restored = pickle.loads(b)
print(restored)
print(restored == data, restored is data)
実行結果:
<class 'bytes'> 69
b'\x80\x04\x95:\x00\x00\x00\x00\x00\x00\x00}'
{'name': '田中', 'age': 30, 'tags': ['python', 'pickle']}
True False
ポイントは3つです。
- dumps()の戻り値は
strではなくbytesです - 先頭の
\x80\x04は「プロトコル4で書かれた」という印です - loads()で戻したオブジェクトは中身が等しい(
==がTrue)だけで、別のオブジェクト(isがFalse)です
dumps/loadsとdump/loadの違い
名前の末尾にsが付くかどうかで、相手がbytesかファイルかが変わります。json.dumps()/json.loads()と同じ命名です。
| dumps() / loads() | dump() / load() | |
|---|---|---|
| 書き出し先・読み込み元 | メモリ上のbytes | バイナリモードで開いたファイル |
| 引数 | dumps(obj) / loads(data) | dump(obj, file) / load(file) |
| 戻り値 | dumps()はbytes、loads()はオブジェクト | dump()はNone、load()はオブジェクト |
| 向いている用途 | DB・キャッシュ・通信・別の形式への埋め込み | .pklファイルへの保存 |
ファイルに保存するだけならdump()/load()のほうが短く書けます。dumps()を使うのは、bytesのまま別の場所へ渡したいときです。
pickle.dumps()とjson.dumps()の違い
どちらも「オブジェクトを文字の並びに変える」関数ですが、結果の型と扱える範囲が違います。
| pickle.dumps() | json.dumps() | |
|---|---|---|
| 戻り値 | bytes | str |
| 人が読めるか | 読めない | 読める |
| 扱える型 | Pythonのほとんどのオブジェクト(set・tuple・date・自作クラスなど) | dict・list・str・数値・True/False・Noneが中心 |
| 他の言語から読めるか | 基本的に読めない(Python専用) | 読める |
| 信頼できないデータを読んでよいか | 読んではいけない | 読んでも任意コードは実行されない |
import json
import pickle
from datetime import date
data = {"day": date(2026, 10, 18), "ids": {1, 2, 3}, "pair": (1, 2)}
b = pickle.dumps(data)
print(pickle.loads(b))
try:
json.dumps(data)
except TypeError as e:
print("TypeError:", e)
実行結果:
{'day': datetime.date(2026, 10, 18), 'ids': {1, 2, 3}, 'pair': (1, 2)}
TypeError: Object of type date is not JSON serializable
pickleはdateやset、tupleを型ごと元に戻せますが、JSONは日付を扱えずエラーになります。逆に、他の言語のプログラムや外部サービスとデータをやり取りするなら、JSONを選ぶのが基本です。JSON側の使い方は「Python json.dumps()とは?辞書をJSON文字列に変換する方法」を参照してください。
protocol引数とbytesの先頭
dumps()のprotocol引数で、pickleの形式のバージョンを選べます。省略するとpickle.DEFAULT_PROTOCOLが使われます。
import pickle
print(pickle.DEFAULT_PROTOCOL, pickle.HIGHEST_PROTOCOL)
data = [1, 2, 3]
for p in range(0, pickle.HIGHEST_PROTOCOL + 1):
b = pickle.dumps(data, protocol=p)
print(p, len(b), b[:2])
実行結果(Python 3.12):
4 5
0 18 b'(l'
1 12 b']q'
2 14 b'\x80\x02'
3 14 b'\x80\x03'
4 22 b'\x80\x04'
5 22 b'\x80\x05'
プロトコル2以降は先頭が\x80+番号になります。公式ドキュメントによると、既定のプロトコルはPython 3.0で3、3.8で4、3.14で5に変わりました(2026年10月18日確認)。loads()はプロトコルを自動判別するので、読む側で指定する必要はありません。ただし、新しいプロトコルで書いたbytesは古いPythonでは読めないため、古い環境に渡すときはdumps()側でprotocolを下げます。詳しくは「Python pickleのprotocolとは?バージョンの違いと選び方」で解説しています。
bytesを文字列として扱いたいとき(base64)
dumps()の結果はUTF-8の文字列ではないので、decode()はできません。また、loads()にstrを渡すとエラーになります。
import pickle
b = pickle.dumps({"a": 1})
try:
b.decode("utf-8")
except UnicodeDecodeError as e:
print("UnicodeDecodeError:", e)
try:
pickle.loads(str(b))
except TypeError as e:
print("TypeError:", e)
実行結果:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
TypeError: a bytes-like object is required, not 'str'
テキストしか入らない場所(環境変数、JSONの値、テキスト列のDBなど)に入れたいときは、base64で英数字の文字列にします。
import base64
import pickle
b = pickle.dumps({"a": 1})
s = base64.b64encode(b).decode("ascii")
print(s, len(b), len(s))
print(pickle.loads(base64.b64decode(s)))
実行結果:
gASVCgAAAAAAAAB9lIwBYZRLAXMu 21 28
{'a': 1}
21バイトが28文字になりました。base64は3バイトを4文字で表すので、およそ4/3倍に増えます。bytesのまま保存できる場所なら、base64にしないほうが効率的です。base64の詳細は「Python base64とは?バイナリを文字列に変換する方法を解説」を参照してください。
dumps()の結果をDBに保存する例(sqlite3)
dumps()が役に立つ典型が、計算結果をDBやキャッシュにbytesのまま保存する使い方です。標準ライブラリのsqlite3なら、BLOB列にそのまま入れられます。
import pickle
import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE cache (key TEXT PRIMARY KEY, value BLOB)")
result = {"user": "tanaka", "scores": [80, 92, 75]}
conn.execute("INSERT INTO cache VALUES (?, ?)", ("u1", pickle.dumps(result)))
row = conn.execute("SELECT value FROM cache WHERE key = ?", ("u1",)).fetchone()
print(type(row[0]))
print(pickle.loads(row[0]))
実行結果:
<class 'bytes'>
{'user': 'tanaka', 'scores': [80, 92, 75]}
Redisのようにbytesを受け取るキャッシュでも考え方は同じです。ただし、そのDBに外部から書き込める場合、書き換えられたbytesをloads()すると危険です(次の節)。メモリ上でファイルのように読み書きしたい場合は「Python BytesIOとは?メモリ上でバイナリを扱う方法」も参考になります。
なお、multiprocessingはプロセスにデータを渡すときに内部でpickleを使います。その仕組みは「マルチプロセスとは?マルチスレッドとの違いを初心者向けに解説」で扱っています。
loads()は信頼できるbytesにだけ使う
公式ドキュメントは冒頭で、pickleモジュールは安全ではなく、信頼できるデータだけをunpickleするよう警告しています。悪意を持って作られたpickleデータは、loads()した時点で任意のコードを実行できるためです(2026年10月18日確認)。
同じドキュメントでは、改ざんされていないことを確かめたい場合にhmacで署名する方法が挙げられています。次は、署名を付けてから保存し、読み込む前に検証する例です。
import hashlib
import hmac
import pickle
KEY = b"secret-key-kept-on-server"
def sign(obj):
data = pickle.dumps(obj)
mac = hmac.new(KEY, data, hashlib.sha256).digest()
return mac + data
def verify_and_load(blob):
mac, data = blob[:32], blob[32:]
expected = hmac.new(KEY, data, hashlib.sha256).digest()
if not hmac.compare_digest(mac, expected):
raise ValueError("署名が一致しません")
return pickle.loads(data)
blob = sign({"a": 1})
print(verify_and_load(blob))
tampered = blob[:-2] + b"X."
try:
verify_and_load(tampered)
except ValueError as e:
print("ValueError:", e)
実行結果:
{'a': 1}
ValueError: 署名が一致しません
署名が合わないbytesはloads()に渡さずに止めています。そもそも外部の人が送ってくるデータであれば、pickleではなくJSONを使うのが安全です。危険性の詳細は「Python pickleは危険?信頼できないデータを読んではいけない理由」にまとめています。
dumps()・loads()でよく出るエラー
PicklingError:ラムダ式やローカル関数をdumps()した
import pickle
try:
pickle.dumps(lambda x: x * 2)
except Exception as e:
print(type(e).__name__, e)
def double(x):
return x * 2
f = pickle.loads(pickle.dumps(double))
print(f(21))
print(pickle.dumps(double))
実行結果:
PicklingError Can't pickle <function <lambda> at 0x103a39300>: attribute lookup <lambda> on __main__ failed
42
b'\x80\x04\x95\x17\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06double\x94\x93\x94.'
関数は中身ではなく「モジュール名と関数名」で保存されます(最後の行に__main__とdoubleが見えます)。名前で引き直せないラムダ式はdumps()できません。モジュールの最上位にdefで定義し直すと解決します。
AttributeError: Can’t get attribute:読み込む側にクラスが無い
import pickle
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
b = pickle.dumps(Point(1, 2))
del Point
try:
pickle.loads(b)
except AttributeError as e:
print("AttributeError:", e)
実行結果(パスは一部省略):
AttributeError: Can't get attribute 'Point' on <module '__main__' from '/.../p8.py'>
自作クラスのインスタンスも、bytesに入るのはクラスの名前とデータだけです。loads()する側で同じ名前のクラスをimportできないと復元できません。別のスクリプトで読むときは、クラスを共通のモジュールに置いてimportします。モジュール自体が見つからない場合はModuleNotFoundError: No module named ...になります。
TypeError: a bytes-like object is required, not ‘str’
loads()にstrを渡したときのエラーです。DBやファイルからテキストとして読んでいないか確認し、base64で保存しているならbase64.b64decode()で戻してからloads()に渡します。
後ろに余分なデータがあっても無視される
import pickle
b = pickle.dumps([1, 2]) + b"extra"
print(pickle.loads(b))
実行結果:
[1, 2]
公式ドキュメントにあるとおり、pickleの終わりより後ろのbytesは無視されます。複数のオブジェクトを1つのbytesに連結した場合、loads()では先頭の1つしか取り出せません。複数入れたいときは、リストや辞書にまとめてから1回でdumps()します。
Pythonの標準ライブラリを、手を動かしながら順番に身につけたい方は、テックジムの無料講座一覧(無料Python講座など)も利用できます。テックジムは東京本校(永田町)などに通う対面型のプログラミングスクールで、自分のペースで進めながら、詰まったところをトレーナーに質問できます。
よくある質問
Q. pickle.dumps()の戻り値は何型ですか?
bytes型です。str型ではないため、decode()で文字列にすることはできません。テキストとして扱う必要がある場合はbase64でエンコードします。
Q. pickle.dumps()とpickle.dump()はどう使い分けますか?
ファイルに保存するならdump()、DBやキャッシュ、通信などにbytesのまま渡したいならdumps()を使います。読み込みも同様に、ファイルならload()、bytesならloads()です。
Q. pickle.loads()でプロトコルを指定する必要はありますか?
ありません。loads()はbytesに書かれたプロトコルを自動で判別します。古いPythonで読ませたいときは、書き出すdumps()側でprotocolを下げます。
Q. pickle.dumps()とjson.dumps()はどちらを使うべきですか?
Pythonの中だけで使い、dateやset、自作クラスなどを型ごと戻したいならpickle、他の言語や外部とやり取りするなら、人が読めて安全なJSONを使います。
Q. 他人から受け取ったbytesをpickle.loads()してもよいですか?
いけません。悪意のあるpickleデータはloads()の時点で任意のコードを実行できます。信頼できる自分のデータだけを読み込み、改ざんが心配ならhmacで署名を確認します。


