Unpacking a python list in C?
Ken Seehof
kseehof at neuralintegrator.com
Fri May 3 09:27:10 EDT 2002
Michael Hudson wrote:
> Ken Seehof <kseehof at neuralintegrator.com> writes:
>
> > Here's some real code that's pretty close to what you want. You can
> > use PyList_* instead of PySequence_* if speed is more important than
> > flexibility (PySequence works with any sequence object).
> > This example is a little different than what you requested (it's a
> > working python extension function). But it does show how to iterate
> > a python list.
> >
> > /////////////////////////
> > // syntax: obj = manifold([seq])
> > // returns: new manifold object
> > //
> > // Creates a manifold object, and initializes with sequence
>
> Are you not doing any error checking for clarity?
Nah, just plain laziness :-) I think the only error checking I
missed is the sequence type check.
> > static PyObject *module_manifold(PyObject *self, PyObject *args)
> > {
> > PyObject *seq = NULL;
> > if (!PyArg_ParseTuple(args, "|O", &seq)) return NULL;
>
> It would be wise to check that "seq" is indeed a sequence at some
> point...
That's a good idea. Thanks.
> Also, if you call this function with no argument it will return NULL
> but with no exception set... don't do that.
Not true. Actually, calling with no argument is valid for "|O", and
in this case leaves seq=NULL (which skips the sequence initialization
as intended). Calling with incorrect arguments (in this case, two or
more args) results in PyArg_ParseTuple returning NULL, with the
appropriate TypeError set automagically. When my function returns
NULL, I get the correct exception.
>>> manifold([2,3,4])
<2, 3, 4>
>>> manifold()
<>
>>> manifold([2,3,4], 3.4)
Traceback (most recent call last):
File "<input>", line 1, in ?
TypeError: function takes at most 1 argument (2 given)
> > manifold__object *k = manifold__new();
> >
> > if (seq)
> > {
> > for (int i=0; i<PySequence_Length(seq); i++)
>
> Also PySequence_Length isn't reliable; consider
>
> class Devious:
> def __len__(self):
> return 9
> def __getitem__(self, i):
> raise IndexError
>
> > {
> > PyObject *obj = PySequence_GetItem(seq, i);
> > manifold__insert_obj(k, k->root, obj);
>
> ... so obj could be NULL here; manifold__insert_obj will likely try to
> INCREF it... boom!
Yup, boom it is. I'd better fix that. Thanks.
> > }
> > }
> >
> > return (PyObject *)k;
> > }
>
> The best way to nullify the threat of such devious code is to use the
> "PySequence_Fast" API, like so:
>
> static PyObject *module_manifold(PyObject *self, PyObject *args)
> {
> PyObject *seq;
> int len;
> if (!PyArg_ParseTuple(args, "O", &seq)) return NULL;
>
> manifold__object *k = manifold__new();
>
> seq = PySequence_Fast(seq, "manifold: sole argument must be
> sequence");
> if (!seq) return NULL;
>
> len = PySequence_Fast_GET_SIZE(seq);
>
> for (int i=0; i < len; i++) {
> PyObject *obj = PySequence_Fast_GET_ITEM(seq, i);
> manifold__insert_obj(k, k->root, obj);
> }
>
> return (PyObject *)k;
> }
>
> as PySequence_Fast knows how to cope with the most devious of user
> classes. I'm not sure when it appeared (I don't *think* it's in
> 1.5.2, unfortunately, though there's always the option of wholesale
> lifting of code from Python's codebase to your own).
>
> Docs here:
>
> http://www.python.org/dev/doc/devel/api/sequence.html
>
> Mild apologies for picking on Ken's code like this; more people should
> know about this API, though.
Thanks, most of your suggestions are very helpful and all your suggestions
are appreciated. I look forward to putting this stuff on SourceForge
to get more feedback, when I get some free time.
In case you're wondering, "manifold" is a new kind of list.
- safe deletion during iteration
- O(constant) fast __contains__ (in) operator
- global explicit object removal (fast)
- python thread safe
Basically it solves the problem of explicit removal of an object from
all "lists" of which it is a member, especially in situations where
object removal of any kind would normally be hazardous. I've been using
it for a couple weeks, and it seems stable. The sequence initialization
that I posted is one of a few recent additions for completeness.
> Cheers,
> M.
>
> --
> The Programmer's Quick Guide To Python (Time Machine version):
> You try to shoot yourself in the foot, only to realize that
> there's no need, since Guido thoughtfully shot you in the foot
> years ago. -- Nick Mathewson, comp.lang.python
> --
> http://mail.python.org/mailman/listinfo/python-list
Thanks,
- Ken Seehof
More information about the Python-list
mailing list