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で署名を確認します。

あわせて読みたい

対面・無料|東京・永田町で毎週土曜日13時

1回の参加で、プログラミングの構造がわかる

ゼロからはじめるPython入門講座

8年間・累計600回開催/これまで全国で2万人以上が参加

「やはり対面で聞けるのはとても気づきがあり、価値があると思います」

参加者の声

  • 本当に初心者からの講座です。プログラミングに触ったことがなくて大丈夫です
  • 対面だから、その場で聞けます。つまずいた所を持ち帰らずに解決できます
  • 講座は1時間。そのあとも残って学習を続けられ、講師にも質問できます
  • 独学で挫折した方も。糸口が見つかって、講座のあと会員になる方が多くいます

無料で参加する|開催日程を見る

参加費は無料です