简体   繁体   中英

How to convert python array to cython array?

I have an array of float values that is created in regular Python, that I want to pass to a cython function that fronts for an underlying C function. The C function requires the array to be passed as a floating pointer as in:

void setOverlays(const float * verts);

the cython wrapper looks like this:

def set_overlays(verts):
    setOverlays(verts)

How can I make verts into a cython array? I thought that this might work:

cdef float * cVerts = [v for v in verts]

but unfortunately the value generated is a Python object and in this case automatic conversion does not work.

The equivalent expression(that works) in ctypes is:

cVerts = (c_float * len(verts))()
for i in range(len(verts)):
    cVerts[i] = verts[i]
setOverlays(cast(byteref(cVerts), POINTER(c_float)))

I am trying to achieve the same thing, but in cython

Thanks in advance!

I believe you can do this by iterating over the python list of floats and putting them in a C array.

cimport cython
from libc.stdlib cimport malloc, free

cdef:
    float * cfloats
    int i

cfloats = <float *> malloc(len(pyfloats)*cython.sizeof(float))
if cfloats is NULL:
  raise MemoryError()
for i in xrange(len(pyfloats)):
  cfloats[i] = pyfloats[i]
setOverlays(cfloats)
free(cfloats)

The accepted answer is wrong and leads to a segmentation fault, because the memory for the float * is never allocated.

The answer of @JAB shows the way to go, but I would like to elaborate more.

Passing an array:

The question is how to convert python array to an c-style array. Python array (from the array module ) is a wrapper around continuous memory, so there is no need to copy the memory - we just can pass the pointer to the c-function:

from cpython cimport array
import array

def set_overlays(array.array verts):
    setOverlays(verts.data.as_floats)

Using array module is better than numpy because it is part of the standard library - there is no need to install something. This solution is short and nice, but has a problem: somebody could use it with an int-array and there will be no error - the memory just gets reinterpreted. Sometimes it is what one wants, but most of the time this is not the case.

To make sure, that the passed array-object has the right type of data, memory views can be used:

from cpython cimport array #still needed

def set_overlays_memview(float[::1] verts):
    setOverlays(&verts[0])

[::1] ensures , that the memory view wraps around continuous memory. However, this code has a problem if there are no elements in the memory view,because of the out-of-bounds-access verts[0] . The right way to handle it depends on the function setOverlays and is not a part of this answer.

Passing a list:

If we have to pass a python list to the c-function, we have to copy the values to a continuous memory. Best done using the functionality of array - there is no need to reinvent the wheel:

from cpython cimport array
import array

def set_overlays_list(list verts):
    cdef array.array a =  array.array('f', verts)
    setOverlays(a.data.as_floats) #we already know they are floats

Check out http://docs.cython.org/src/userguide/memoryviews.html . Looks like it also gives tips on how to utilize Python's built-in array module, as well as numpy .

I found the Cython specific way:

cdef float* cVerts = []
    for i in xrange(len(verts)):
        cVerts[i] = verts[i]
setOverlays(cVerts)

Interestingly enough the above two solutions were rejected by the cython compiler.

This is what I use for preparing arrays for passing to Cython, or C/CPP with SWIG.

import numpy as np    
def make_c_array(a):
    """
    Take an input numpy array and convert to being ready for use in C.
    """
    b = []
    for i in range(len(a)):
        b.append(a[i])
    a = np.array(b,dtype=np.dtype('d'),order='C')
    return a

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM