[Pyrex] C-level constructors

Stefan Behnel stefan_ml at behnel.de
Thu Nov 8 07:38:48 CET 2007


David McNab wrote:
> Is there any way to implement an extension type's constructor so that it
> can be called with C data types (even if this means not being able to
> invoke the constructor from Python code outside of the Pyrex
> environment)?
> 
> For example:
> 
>   cdef class Foo:
>       cdef int foo
>       def __init__(self, int foo):
>           self.foo = foo
> 
> 
> For now, I'm implementing my extensions with no __init__ or __cinit__,
> and instead adding a method '_init_', eg:
> 
>    cdef class Foo:
>        cdef unsigned short **audioBufs
>        cdef _init_(self, short **audioBufs):
>            self.audioBufs = audioBufs
> 
> and when constructing, I do:
> 
>    cdef Foo myfoo
>    cdef short **bufs
>    ...
>    myfoo = Foo(); myfoo._init_(bufs)
> 
> But - is there a better way to do this?

In lxml, we use factory functions for this that create the types for us and
initialise them. We actually set their fields directly. Sometimes, we even use
a straight call to "tp_new()" for *very* fast initialisation that doesn't even
call __init__().

This would go into a header file (say, "etree_defs.h"):

--------------------------
static PyObject* __PY_NEW_GLOBAL_EMPTY_TUPLE = NULL;

#define PY_NEW(T) \
     (((PyTypeObject*)(T))->tp_new( \
         (PyTypeObject*)(T), \
         ((__PY_NEW_GLOBAL_EMPTY_TUPLE == NULL) ? \
             (__PY_NEW_GLOBAL_EMPTY_TUPLE = PyTuple_New(0)) : \
             (__PY_NEW_GLOBAL_EMPTY_TUPLE)), \
         NULL))
--------------------------

and this is what we use for factory functions:

--------------------------
cdef extern from "etree_defs.h":
    # macro call to 't->tp_new()' for fast instantiation
    cdef _Document NEW_DOCUMENT "PY_NEW" (object t)

cdef _Document _documentFactory(xmlDoc* c_doc, _BaseParser parser):
    cdef _Document result
    result = NEW_DOCUMENT(_Document)
    result._c_doc = c_doc
    result._ns_counter = 0
    result._prefix_tail = None
    result._parser = parser
    return result
--------------------------

This is about as fast as you can get. Note that we actually declare PY_NEW for
each type where we use this pattern to prevent Pyrex from adding type checks.
If you do not need maximum speed type creation, you can just call "MyType()"
instead.

Stefan




More information about the Pyrex mailing list