The copyreg module registers functions used by pickle to serialize types that do not define or expose their own reduction behavior. It is especially useful for native extension types, third-party classes, wrappers, and objects whose serialized form must be defined outside the class.
For classes you control, methods such as __reduce__(), __reduce_ex__(), __getstate__(), and __setstate__() are usually easier to discover and maintain. Use copyreg when external registration is a deliberate design choice. Also remember that pickle is unsafe for untrusted input because loading a payload can execute code.
How pickle reconstructs objects
Pickle does not save a raw memory image. It describes how to rebuild an object using a callable, arguments, optional state, sequence items, and mapping pairs.
A simple reduction function returns a callable and an argument tuple.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def rebuild_point(x, y):
return Point(x, y)
def reduce_point(obj):
return rebuild_point, (obj.x, obj.y)
copyreg.pickle() associates that reducer with the type.
Register a type
import copyreg
import pickle
copyreg.pickle(Point, reduce_point)
data = pickle.dumps(Point(2, 5))
restored = pickle.loads(data)
print(restored.x, restored.y)
The registration is process-global and affects later serialization of that type. Perform it during predictable application initialization.
The constructor must be importable
Functions used for reconstruction must normally be importable by module name when a pickle is loaded in another process. Local functions, lambdas, and closures are poor choices.
Define reconstructors at module level and keep their paths stable. Moving or renaming them can break old data.
Register once
The reducer must remain available while objects are serialized. Avoid registering functions inside requests or temporary scopes.
def configure_serialization():
copyreg.pickle(Point, reduce_point)
Call the setup once during package or process initialization.
Additional state
A reduction tuple may include a third item containing state. After constructing the instance, pickle applies that state through __setstate__() or by updating __dict__ when appropriate.
def reduce_document(obj):
state = {"title": obj.title, "tags": obj.tags}
return Document, (), state
The reconstruction callable must accept the provided arguments.
Objects with __slots__
Slotted classes may need explicit state because they do not use a normal instance dictionary.
def reduce_user(obj):
return create_user, (obj.id, obj.name)
Do not depend on private memory layout details. Define a logical and stable representation.
Native extension types
copyreg is common when a type comes from C or a third-party package and cannot be modified. A Python reducer can extract necessary values and select a reconstruction callable.
Handles, pointers, connections, and operating-system resources should not be serialized as though they were portable. Preserve data or identifiers instead.
Do not serialize active resources
Sockets, open files, locks, threads, processes, and database connections cannot be safely reconstructed from raw state.
Serialize configuration or identifiers. The restored process opens a new resource and handles connection failure.
Version compatibility
Pickles may remain stored for years. Changes to the callable, arguments, or state can make old data unreadable.
Include an explicit version in state and maintain migration paths.
state = {
"version": 2,
"name": obj.name,
"options": obj.options,
}
The reconstructor or __setstate__() can accept earlier versions.
Pickle protocols
The selected protocol affects efficiency and available features. A reducer registered by copyreg does not automatically receive the protocol number.
When behavior must vary by protocol, implementing __reduce_ex__(protocol) on a class may be more appropriate.
copyreg versus methods on the class
For a class you own, keeping serialization behavior inside the class often improves discoverability.
class Point:
def __reduce__(self):
return type(self), (self.x, self.y)
Use external registration to integrate third-party types, separate policies, or avoid changing a public API.
Custom dispatch tables
A custom Pickler can use its own dispatch_table, enabling local behavior without modifying the global registry.
import copyreg
import io
import pickle
buffer = io.BytesIO()
pickler = pickle.Pickler(buffer)
pickler.dispatch_table = copyreg.dispatch_table.copy()
pickler.dispatch_table[Point] = reduce_point
pickler.dump(Point(1, 2))
This is preferable when separate libraries require different policies.
Avoid global conflicts
Two packages can register different reducers for the same type. A later registration may alter behavior across the whole process.
Reusable libraries should prefer local dispatch tables. If a global registration is unavoidable, document it and add integration tests.
constructor
copyreg.constructor() marks a callable as a constructor for selected historical mechanisms. Direct use is rare in modern code.
The word “constructor” does not make unpickling safe. Deserialization can still execute callables.
Extension codes
add_extension(), remove_extension(), and clear_extension_cache() manage compact numeric codes for global references in pickles.
import copyreg
copyreg.add_extension("mypackage.models", "Point", 1001)
Codes belong to a global registry and must be coordinated. Collisions can cause errors or incorrect reconstruction.
Code governance
Do not choose random extension numbers in distributed libraries without a policy. One code must always identify the same module and name.
Removing or reusing a code can make old pickles dangerously ambiguous.
Extension cache
The unpickler may cache extension-code resolution. clear_extension_cache() clears that state, mainly in tests or specialized scenarios.
Do not clear it repeatedly in production. That can hurt performance without solving compatibility.
Pickle security
Never pass data from users, networks, untrusted files, or shared buckets to pickle.loads(). The format can request calls to functions during reconstruction.
A cryptographic signature verifies source and integrity only when keys and senders are trusted. For interoperable data, prefer JSON, MessagePack, or another non-executing format.
Validate after loading
Even a trusted pickle may be outdated or corrupted. Validate types, limits, versions, and invariants after loading.
Do not assume registered functions receive reasonable arguments. A manipulated payload inside a trusted environment can still consume excessive resources.
Size limits
Pickle can encode huge and deeply nested structures. Limit file size before loading and perform risky processing inside controlled CPU and memory boundaries.
There is no universal safe-depth argument for loads().
Multiprocessing
Process pools serialize tasks and results. A copyreg registration can make a type transferable, but workers must import the registration code too.
Place setup in an importable module and test with the spawn start method. See Python multiprocessing.
Large objects
A custom reduction can shrink payloads by saving only essential state. Measure size and time before and after.
Do not trade compatibility and clarity for a minor optimization. Some redundancy can make migrations easier.
Inheritance
A registration for one class should not be assumed to describe every subclass. Subclasses may add state or invariants.
Test exact types and decide whether each subclass needs its own reducer.
Dataclasses and named tuples
Many ordinary Python types already pickle correctly. Do not add a reducer simply because an object contains several fields.
Use copyreg when default serialization is impossible, unstable, or semantically wrong.
Round-trip tests
A basic test serializes, deserializes, and compares state and behavior.
original = Point(2, 5)
restored = pickle.loads(pickle.dumps(original))
assert (restored.x, restored.y) == (2, 5)
Include several protocols, separate processes, old versions, and invalid states.
Test in a fresh process
A round trip in one process can hide already imported modules and registrations. Load the data inside a clean subprocess or integration environment.
This exposes local functions, incorrect module paths, and missing initialization.
Observability
Record type, state version, protocol, payload size, and duration without logging the full pickle. Payloads may contain secrets.
Reconstruction failures should include an object or job identifier, not raw bytes.
Common mistakes
Common failures include registering lambdas, moving the reconstructor without migration, serializing active handles, loading untrusted data, changing global dispatch inside a library, ignoring subclasses, reusing extension codes, and testing only in the same process.
Conclusion
copyreg teaches pickle how to reduce and rebuild types that do not own their serialization behavior. Use stable module-level functions, versioned state, local dispatch tables where possible, and clean-process tests.
Never use pickle as a format for untrusted input. Consult the official copyreg documentation and the pickle documentation.







