boost::python , dispatching in python

David Abrahams david.abrahams at rcn.com
Wed Jan 9 19:10:29 CET 2002


----- Original Message -----
From: "Arnaldur Gylfason" <arnaldur at decode.is>

> I've been looking at the python interpreter
> (parsetok,pythonrun,tokenizer,ceval,compile,...).
> It's a lot of code!

With few comments!

> In ceval the operation is selected on the opcode that the tokenizer or
> something similar sets.
> I didn't see the place where the opcode is selected on the symbol.
> However the following in ceval handle, a + b and a[i]:
>
>           case BINARY_ADD:
>                w = POP();
>                v = POP();
>                if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
>                     /* INLINE: int + int */
>                     register long a, b, i;
>                     a = PyInt_AS_LONG(v);
>                     b = PyInt_AS_LONG(w);
>                     i = a + b;
>                     if ((i^a) < 0 && (i^b) < 0)
>                          goto slow_add;
>                     x = PyInt_FromLong(i);
>                }
>                else {
>                  slow_add:
>                     x = PyNumber_Add(v, w);
>                }
>                Py_DECREF(v);
>                Py_DECREF(w);
>                PUSH(x);
>                if (x != NULL) continue;
>                break;

So it looks like PyNumber_Add is the way to go for a + b

>           case BINARY_SUBSCR:
>                w = POP();
>                v = POP();
>                if (PyList_CheckExact(v) && PyInt_CheckExact(w)) {
>                     /* INLINE: list[int] */
>                     long i = PyInt_AsLong(w);
>                     if (i < 0)
>                          i += PyList_GET_SIZE(v);
>                     if (i < 0 ||
>                         i >= PyList_GET_SIZE(v)) {
>                          PyErr_SetString(PyExc_IndexError,
>                               "list index out of range");
>                          x = NULL;
>                     }
>                     else {
>                          x = PyList_GET_ITEM(v, i);
>                          Py_INCREF(x);
>                     }
>                }
>                else
>                     x = PyObject_GetItem(v, w);
>                Py_DECREF(v);
>                Py_DECREF(w);
>                PUSH(x);
>                if (x != NULL) continue;
>                break;
>
> We can see that BINARY_SUBSCR tries list access first but key access
> otherwise (PyObject_GetItem).
> sequence access thus should probably have precedence over key access if
key
> is an integer (when an object has both sequence and mapping interfaces)

That's the wrong conclusion to draw. The check for list[int] is purely an
optimization, just like the check for int+int in the first case.
PyObject_GetItem() is the simple generalized routine that works for both
sequence and mapping protocols:

PyObject *
PyObject_GetItem(PyObject *o, PyObject *key)
{
 PyMappingMethods *m;

 if (o == NULL || key == NULL)
  return null_error();

 m = o->ob_type->tp_as_mapping;
 if (m && m->mp_subscript)
  return m->mp_subscript(o, key);

 if (o->ob_type->tp_as_sequence) {
  if (PyInt_Check(key))
   return PySequence_GetItem(o, PyInt_AsLong(key));
  else if (PyLong_Check(key)) {
   long key_value = PyLong_AsLong(key);
   if (key_value == -1 && PyErr_Occurred())
    return NULL;
   return PySequence_GetItem(o, key_value);
  }
  return type_error("sequence index must be integer");
 }

 return type_error("unsubscriptable object");
}


> BINARY_ADD calls PyNumber_Add.
<snip>
> PyNumber_Add is :
>

> It tries nb_add but sequence concat if it fails.

Okay.

> > > Yes. I'll take a look at the python source.
> > > In some cases we know how to handle things like o[i]: sequence access
> if i
> > > is integral, mapping access otherwise.
> >
> > Not neccessarily:
> >
> > x = { 1 : 'one', 2 : 'two' }
> > x[1]
>
> x is a dictionary so it only supports the mapping interface. No conflict
> with the sequence interface in this case.

There's no conflict, but the procedure you specified wouldn't work. Just use
PyObject_GetItem(). Almost every operation with a possible slot conflict has
a similar dispatcher, I'll wager.

-Dave





More information about the Cplusplus-sig mailing list