__reduce__ / __reduce_ex__
Signature and Return Value
__reduce__ and __reduce_ex__ control how Python serializes your objects. Pickle, copy, and shelve all call these methods to pack and rebuild your data.
def __reduce__(self) -> str | Tuple
def __reduce_ex__(self, protocol: int) -> str | Tuple
__reduce__ takes no arguments beyond self. __reduce_ex__ receives the pickle protocol version. protocol is an int specifying the pickle protocol version (0–5); the method can branch on this value to produce different output per protocol.
Both methods return either a string or a tuple.
String Return
If you return a string, pickle treats it as the name of a global variable and stores whatever object that name refers to. This is the simplest path and works well for singletons.
import pickle
SPAM = "eggs"
class Wrapper:
def __init__(self, value):
self.value = value
def __reduce__(self):
return "SPAM" # lookup the global named SPAM
w = Wrapper(42)
data = pickle.dumps(w)
restored = pickle.loads(data)
print(restored) # 'eggs'
Tuple Return (2–5 items)
Returning a tuple gives you full control over reconstruction. The tuple must contain 2 to 5 items:
| Items | Format | What happens |
|---|---|---|
| 2 | (callable, args) | Calls callable(*args) to build the object |
| 3 | (callable, args, state) | Same, then calls obj.__setstate__(state) |
| 4 | (callable, args, state, list_items) | Same as 3, then extends the object with list_items |
| 5 | (callable, args, state, list_items, dict_items) | Same as 4, then updates the object with dict_items |
Any trailing items you don’t need can be omitted or set to None.
Minimal Example: 2-Item Tuple
The most common pattern returns the class and its construction arguments:
import pickle
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __reduce__(self):
return (self.__class__, (self.x, self.y))
p = Point(3, 4)
data = pickle.dumps(p)
restored = pickle.loads(data)
print(restored.x, restored.y) # 3 4
Restoring State with __setstate__
When you return a 3-item tuple, pickle calls __setstate__ on the reconstructed object with your provided state. This separates reconstruction (creating the object) from restoring its internal state.
import pickle
class FrozenPoint:
def __init__(self, x, y):
self.x = x
self.y = y
self._frozen = True
def __reduce__(self):
return (
self.__class__,
(0, 0), # args for __init__ (ignored)
{'x': self.x, 'y': self.y, '_frozen': self._frozen}
)
def __setstate__(self, state):
self.x = state['x']
self.y = state['y']
self._frozen = state['_frozen']
fp = FrozenPoint(10, 20)
data = pickle.dumps(fp)
restored = pickle.loads(data)
print(restored.x, restored.y, restored._frozen) # 10 20 True
__init__ received (0, 0) — values we threw away. The real data came through __setstate__.
__reduce_ex__ for Protocol-Aware Serialization
__reduce_ex__ receives the pickle protocol version, letting you adjust behavior across Python versions. When __reduce_ex__ is defined, pickle prefers it over __reduce__.
import copy
import pickle
class Node:
def __init__(self, value, children=None):
self.value = value
self.children = children or []
def __reduce_ex__(self, protocol):
return (
self.__class__,
(self.value, []),
None, # no custom state
self.children, # list_items: restore the children list
None # no dict_items
)
def __setstate__(self, state):
pass # state is None, nothing to restore
root = Node("root", [Node("leaf")])
copied = copy.deepcopy(root)
print(copied.children[0].value) # leaf
What Calls __reduce__
These methods are invoked by several stdlib tools:
pickle.dump()/pickle.dumps()/pickle.load()/pickle.loads()copy.copy()andcopy.deepcopy()shelve.open()(which uses pickle under the hood)marshall(deprecated)
Common Gotchas
__setstate__ is mandatory when returning state.
If your tuple has 3+ items and you include state, your class must define __setstate__. Omitting it raises AttributeError during unpickling.
Returning mutable state as the same reference.
If you return a list or dict as your state and intend to modify it in __setstate__, pickle may treat it as the same object. Return a copy of mutable state to avoid subtle aliasing bugs.
__slots__ without __dict__.
Classes using __slots__ and no __dict__ need to return slot values as a plain tuple or primitives. The reconstruction callable must handle slot restoration explicitly in __setstate__.
Security: never unpickle untrusted data.
__reduce__ can return arbitrary callables. A malicious pickle payload can execute arbitrary code during unpickling. Use json or similar safe formats when data comes from untrusted sources.
See Also
- /reference/dunder-methods/dunder-hash/ — controls object hashing, used by dicts and sets
- /reference/dunder-methods/dunder-new/ — controls object creation before
__init__ - /reference/modules/pickle-module/ — the module that drives these dunder methods