Introducing __reduce_ex__, which is called with a protocol number argument
authorGuido van Rossum <guido@python.org>
Tue, 18 Feb 2003 22:05:12 +0000 (22:05 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 18 Feb 2003 22:05:12 +0000 (22:05 +0000)
if it exists in preference over __reduce__.  Now Tim can go implement this
in cPickle.c.

Lib/copy_reg.py
Lib/pickle.py
Lib/test/test_descrtut.py
Objects/typeobject.c

index 6dc52c235d4afcbff62cbc55a0b94f9db7b61b20..fa4ce72d6a37809465cfe729fe6e7f0b13f16573 100644 (file)
@@ -109,6 +109,17 @@ def _better_reduce(obj):
         dictitems = obj.iteritems()
     return __newobj__, (cls,) + args, state, listitems, dictitems
 
+# Extended reduce:
+
+def _reduce_ex(obj, proto=0):
+    obj_reduce = getattr(obj, "__reduce__", None)
+    if obj_reduce and obj.__class__.__reduce__ is not object.__reduce__:
+        return obj_reduce()
+    elif proto < 2:
+        return _reduce(obj)
+    else:
+        return _better_reduce(obj)
+
 def _slotnames(cls):
     """Return a list of slot names for a given class.
 
index c62bddc4efc26d111f49b3e489e0c27a7dce6034..f997f38d7ef227cb0bb180a16d82c6b513b68beb 100644 (file)
@@ -304,21 +304,20 @@ class Pickler:
 
         # Check copy_reg.dispatch_table
         reduce = dispatch_table.get(t)
-        if not reduce:
-            # Check for a __reduce__ method.
-            # Subtle: get the unbound method from the class, so that
-            # protocol 2 can override the default __reduce__ that all
-            # classes inherit from object.  This has the added
-            # advantage that the call always has the form reduce(obj)
-            reduce = getattr(t, "__reduce__", None)
-            if self.proto >= 2:
-                # Protocol 2 can do better than the default __reduce__
-                if reduce is object.__reduce__:
-                    reduce = _better_reduce
-            if not reduce:
-                raise PicklingError("Can't pickle %r object: %r" %
-                                    (t.__name__, obj))
-        rv = reduce(obj)
+        if reduce:
+            rv = reduce(obj)
+        else:
+            # Check for a __reduce_ex__ method, fall back to __reduce__
+            reduce = getattr(obj, "__reduce_ex__", None)
+            if reduce:
+                rv = reduce(self.proto)
+            else:
+                reduce = getattr(obj, "__reduce__", None)
+                if reduce:
+                    rv = reduce()
+                else:
+                    raise PicklingError("Can't pickle %r object: %r" %
+                                        (t.__name__, obj))
 
         # Check for string returned by reduce(), meaning "save as global"
         if type(rv) is StringType:
index 66cb74924d9b57392a3f8e6bb7e00ca34f4d2424..5fa089790e50a7b3f71d95b60507e3295526f671 100644 (file)
@@ -210,6 +210,7 @@ Instead, you can get the same information from the list type:
      '__ne__',
      '__new__',
      '__reduce__',
+     '__reduce_ex__',
      '__repr__',
      '__rmul__',
      '__setattr__',
index e238056c8baf19b9b43d9c647d8a2814a95e9cc1..a1e5b88ad77c7a142641cf8de70718791d17e38e 100644 (file)
@@ -2441,11 +2441,15 @@ static PyGetSetDef object_getsets[] = {
 };
 
 static PyObject *
-object_reduce(PyObject *self, PyObject *args)
+object_reduce_ex(PyObject *self, PyObject *args)
 {
-       /* Call copy_reg._reduce(self) */
+       /* Call copy_reg._reduce_ex(self, proto) */
        static PyObject *copy_reg_str;
        PyObject *copy_reg, *res;
+       int proto = 0;
+
+       if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto))
+               return NULL;
 
        if (!copy_reg_str) {
                copy_reg_str = PyString_InternFromString("copy_reg");
@@ -2455,13 +2459,15 @@ object_reduce(PyObject *self, PyObject *args)
        copy_reg = PyImport_Import(copy_reg_str);
        if (!copy_reg)
                return NULL;
-       res = PyEval_CallMethod(copy_reg, "_reduce", "(O)", self);
+       res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto);
        Py_DECREF(copy_reg);
        return res;
 }
 
 static PyMethodDef object_methods[] = {
-       {"__reduce__", object_reduce, METH_NOARGS,
+       {"__reduce_ex__", object_reduce_ex, METH_VARARGS,
+        PyDoc_STR("helper for pickle")},
+       {"__reduce__", object_reduce_ex, METH_VARARGS,
         PyDoc_STR("helper for pickle")},
        {0}
 };