pickletools
The Python pickletools module contains helpers for analyzing and optimizing data produced by the pickle module. It can disassemble a pickle stream into a human-readable listing of opcodes, iterate over the individual operations, or strip unused memo entries to produce a smaller stream. Because it inspects the bytes of a pickle without actually reconstructing the stored objects, it is also a safer way to look inside an untrusted pickle than pickle.load() itself.
Here’s a quick example:
>>> import pickle, pickletools
>>> blob = pickle.dumps((1, 2))
>>> pickletools.dis(blob)
0: \x80 PROTO 5
2: \x95 FRAME 7
11: K BININT1 1
13: K BININT1 2
15: \x86 TUPLE2
16: \x94 MEMOIZE (as 0)
17: . STOP
highest protocol among opcodes = 4
Each line corresponds to one pickle opcode, along with its position in the stream and its decoded argument.
Key Features
- Disassembles a pickle stream into a readable opcode listing with
dis() - Iterates over individual opcodes, arguments, and positions through
genops() - Rewrites a pickle with
optimize()to remove unusedPUTandMEMOIZEoperations - Supports a command-line entry point invoked with
python -m pickletools - Reads pickle data from either a
bytesobject or any binary file-like object - Operates without importing or instantiating the objects stored in the pickle, which avoids executing arbitrary code
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
pickletools.dis() |
Function | Writes a symbolic disassembly of a pickle to an output stream |
pickletools.genops() |
Function | Yields (opcode, arg, position) tuples for every operation in a pickle |
pickletools.optimize() |
Function | Returns an equivalent pickle with unused memo slots removed |
Examples
Disassembling a pickle that contains a small dictionary:
>>> import pickle, pickletools
>>> blob = pickle.dumps({"name": "Ada", "tags": [1, 2, 3]})
>>> pickletools.dis(blob)
0: \x80 PROTO 5
2: \x95 FRAME 35
11: } EMPTY_DICT
12: \x94 MEMOIZE (as 0)
13: ( MARK
14: \x8c SHORT_BINUNICODE 'name'
20: \x94 MEMOIZE (as 1)
21: \x8c SHORT_BINUNICODE 'Ada'
26: \x94 MEMOIZE (as 2)
27: \x8c SHORT_BINUNICODE 'tags'
33: \x94 MEMOIZE (as 3)
34: ] EMPTY_LIST
35: \x94 MEMOIZE (as 4)
36: ( MARK
37: K BININT1 1
39: K BININT1 2
41: K BININT1 3
43: e APPENDS (MARK at 36)
44: u SETITEMS (MARK at 13)
45: . STOP
highest protocol among opcodes = 4
Adding short descriptions to each opcode with annotate=1:
>>> import pickle, pickletools
>>> blob = pickle.dumps(1)
>>> pickletools.dis(blob, annotate=1)
0: \x80 PROTO 5 Protocol version indicator.
2: K BININT1 1 Push a one-byte unsigned integer.
4: . STOP Stop the unpickling machine.
highest protocol among opcodes = 2
Iterating over the opcodes programmatically with the genops() function:
>>> import pickle, pickletools
>>> blob = pickle.dumps([10, 20])
>>> for opcode, arg, pos in pickletools.genops(blob):
... print(pos, opcode.name, arg)
...
0 PROTO 5
2 FRAME 9
11 EMPTY_LIST None
12 MEMOIZE None
13 MARK None
14 BININT1 10
16 BININT1 20
18 APPENDS None
19 STOP None
Shrinking a pickle by removing unused memo slots:
>>> import pickle, pickletools
>>> blob = pickle.dumps({"name": "Ada", "tags": [1, 2, 3]})
>>> optimized = pickletools.optimize(blob)
>>> len(blob), len(optimized)
(46, 41)
>>> pickle.loads(optimized) == pickle.loads(blob)
True
Running the module from the command line on a saved pickle file:
$ python -m pickletools data.pickle
0: \x80 PROTO 5
2: \x95 FRAME 7
11: K BININT1 1
13: K BININT1 2
15: \x86 TUPLE2
16: \x94 MEMOIZE (as 0)
17: . STOP
highest protocol among opcodes = 4
Common Use Cases
The most common tasks for pickletools include:
- Debugging unexpected output or errors from
pickle.dump()andpickle.load() - Inspecting untrusted pickle files without importing or executing their contents
- Teaching or learning how the pickle protocol encodes Python objects
- Shrinking persisted pickles or network payloads with
optimize() - Comparing different pickle protocols to see how they encode the same value
Real-World Example
Consider a helper that groups every opcode in a pickle by name and reports the most frequent operations, which is a handy first step when profiling a large persisted data set:
>>> import pickle, pickletools
>>> from collections import Counter
>>> users = [{"id": i} for i in range(4)]
>>> blob = pickle.dumps(users)
>>> counts = Counter(op.name for op, _, _ in pickletools.genops(blob))
>>> counts.most_common(4)
[('MEMOIZE', 6), ('EMPTY_DICT', 4), ('BININT1', 4), ('SETITEM', 4)]
The helper uses genops() to walk through the stream exactly once, extracts the opcode name for each step, and feeds the names into a Counter to rank them. No object in the pickle is ever reconstructed, which makes this kind of analysis safe to run on pickles that came from an untrusted source.
Related Resources
Tutorial
The Python pickle Module: How to Persist Objects in Python
In this tutorial, you'll learn how you can use the Python pickle module to convert your objects into a stream of bytes that can be saved to a disk or sent over a network. You'll also learn the security implications of using this process on objects from an untrusted source.
For additional information on related topics, take a look at the following resources:
- Serialize Your Data With Python (Tutorial)
- Working With JSON Data in Python (Tutorial)
- Bytes Objects: Handling Binary Data in Python (Tutorial)
- Python's collections: A Buffet of Specialized Data Types (Tutorial)
- Serializing Objects With the Python pickle Module (Course)
- Working With JSON in Python (Course)
- Working With JSON Data in Python (Quiz)
- Python Bytes (Quiz)
By Leodanis Pozo Ramos • Updated May 7, 2026