implementing reduce protocol in C

Alex Martelli aleax at aleax.it
Fri Sep 20 10:32:41 EDT 2002


Tom Widrick wrote:
        ...
> static PyObject *
> proto_reduce(self)
> HyProtoObject *self;

I recommend you use "ANSI C" style as a habit:

static PyObject*
proto_reduce(HyProtoObject* self)

it may not matter here, but one day you'll be glad you got into
this habit.  And since Python does now require an ANSI/ISO
compatible C compiler, there's no downside.

> {
> PyObject *args;
> PyObject *result;
> 
> args = PyTuple_New(5);
> if(args == NULL)
> return NULL;
> 
> PyTuple_SET_ITEM(args, 0, self->po_name);
> Py_INCREF(self->po_name);
> PyTuple_SET_ITEM(args, 1, self->po_parents);
> Py_INCREF(self->po_parents);
> PyTuple_SET_ITEM(args, 2, self->po_children);
> Py_INCREF(self->po_children);
> PyTuple_SET_ITEM(args, 3, self->po_doc);
> Py_INCREF(self->po_doc);
> PyTuple_SET_ITEM(args, 4, self->po_dict);
> Py_INCREF(self->po_dict);
> 
> result = Py_BuildValue("(OOO)", self->ob_type, Py_None, args);
> Py_DECREF(args);
> return result;
> };
> 
> Any better?

Much better, I think.  I believe you can save the last
Py_DECREF by using "(OON)" as the first arg to Py_BuildValue,
telling it to steal a reference to the third argument, but
that's not crucial.


Back to your main problem however -- things are NOT rosy.
Check out:
http://mail.python.org/pipermail/python-bugs-list/2002-March/010707.html
If I read this correctly, the case where __reduce__ returns a
second argument of None is being deprecated and is not well
supported anyway.  Thus, you probably want to avoid it.  I think
this makes the rest of the discussion moot.

Rather, you probably want to expose a factory function to be
passed as the first item in the tuple returned by __reduce__
(you may then want to return a 2-items tuple rather than a
3-items one from __reduce__, perhaps).  Just make sure said
first item exposes an attribute named __safe_for_unpickling__ 
with a true value, or register it as a "safe constructor" with
module copy_reg.

(As an aside -- this is the kind of tasks that I usually handle
with a PyRun_String call in my C-coded-module's init function:
they're SO much easier to express in Python, and they're one-off,
thus performance-insensitive tasks anyway; however, I've been
criticized by important and knowledgeable Pythonistas for this
habit -- didn't really understand the reason for the criticism,
but, anyway, use this hint at your own risk:-).


Alex




More information about the Python-list mailing list