[Python-Dev] Pickle implementation questions

Tim Peters tim.peters at gmail.com
Fri Jun 30 20:29:43 CEST 2006


[Bruce Christensen]
> So just to be clear, is it something like this?

I hope you've read PEP 307:

    http://www.python.org/dev/peps/pep-0307/

That's where __reduce_ex__ was introduced (along with all the rest of
pickle protocol 2).

> class object:
>     def __reduce__(self):
>         return copy_reg._reduce_ex(self, -1)
>
>     def __reduce_ex__(self, protocol):
>         return copy_reg._reduce_ex(self, protocol)

The implementation is more like:

class object:
    def __common_reduce__(self, proto=0):

        if self.__class__.__reduce__ is not object.__reduce__:
            # The class overrode __reduce__, so call the override.
            # From PEP 307:
            #     The 'object' class implements both __reduce__ and
            #     __reduce_ex__;  however, if a subclass overrides __reduce__
            #     but not __reduce_ex__,  the __reduce_ex__ implementation
            #     detects this and calls   __reduce__.
            return self.__reduce__()

        elif proto < 2:
            return copy_reg._reduce_ex(self, proto)

        else:
            # about 130 lines of C code exploiting proto 2

    __reduce__ = __reduce_ex__ = __common_reduce__

> Does _reduce_ex's behavior actually change depending on the specified protocol
> version? The only difference that I can see or think of is that an assert causes it to
> fail if the protocol is >= 2.

That's right.  As above, the object reduce methods never call
copy_reg._reduce_ex() when proto >= 2.

Note that __reduce_ex__ doesn't exist for the _benefit_ of object:  it
was introduced in protocol 2 for the benefit of user classes that want
to exploit protocol-specific pickle opcodes in their own __reduce__
methods.  They couldn't do that using the old-time __reduce__ because
__reduce__ wasn't passed the protocol version.

copy_reg._reduce_ex exists only because Guido got fatally weary of
writing mountains of C code, so left what "should be" a rarely-taken
path coded in Python.

>>>  - What does copy_reg.constructor() do?

>> It does this:
>>
>> def constructor(object):
>>     if not callable(object):
>>         raise TypeError("constructors must be callable")

> So it is part of the public interface? It's exported in __all__, but it appears
> that it's undocumented.

It's documented in the Library Reference Manual, in the `copy_reg` docs:

"""
constructor(object)

Declares object to be a valid constructor. If object is not callable
(and hence not valid as a constructor), raises TypeError.
"""

Unfortunately, while all the "safe for unpickling?" gimmicks (of which
this is one -- see PEP 307 again) were abandoned in Python 2.3, the
docs and code comments still haven't entirely caught up.
copy_reg.constructor() exists now only for backward compatibility (old
code may still call it, but it no longer has any real use).


More information about the Python-Dev mailing list