2018年6月18日 星期一

Python3 and C - Python/C API

Python/C API
test.c
#include <Python.h>

//integer
int add_one(int a){
    return a + 1;
}
static PyObject * py_add_one(PyObject *self, PyObject *args){
    int num;
    if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
    //parameter reference
    //The "i" is a integer, if the "ii" are integers, must input two integer
    return PyLong_FromLong(add_one(num));
}

//string
int test_string (char *str)
{
    printf("C_=%s\n", str);
    return 0;
}
static PyObject * py_test_str(PyObject *self, PyObject *args){
    char *str;
    if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
    return  PyLong_FromString(str, NULL, 0);
    //Py_RETURN_NONE;
}

//array
static double total(double* data, int len)
{
    double total = 0.0;
    int i;
    for(i=0; i<len; ++i)
        total += data[i];
    return total;
}
static PyObject *totalDoubles(PyObject *self, PyObject *args)
{
    PyObject* seq;
    double *dbar;
    double result;
    int seqlen;
    int i;

    /* get one argument as a sequence */
    if(!PyArg_ParseTuple(args, "O", &seq))
        return 0;
    seq = PySequence_Fast(seq, "argument must be iterable"); 
    if(!seq)
        return 0;

    /* prepare data as an array of doubles */
    seqlen = PySequence_Fast_GET_SIZE(seq);
    dbar = malloc(seqlen*sizeof(double));
    if(!dbar) {
        Py_DECREF(seq);
        return PyErr_NoMemory(  );
    }
    for(i=0; i < seqlen; i++) {
        PyObject *fitem;
        PyObject *item = PySequence_Fast_GET_ITEM(seq, i);
        if(!item) {
            Py_DECREF(seq);
            free(dbar);
            return 0;
        }
        fitem = PyNumber_Float(item);
        if(!fitem) {
            Py_DECREF(seq);
            free(dbar);
            PyErr_SetString(PyExc_TypeError, "all items must be numbers");
            return 0;
        }
        dbar[i] = PyFloat_AS_DOUBLE(fitem);
        Py_DECREF(fitem);
    }    

    /* clean up, compute, and return result */
    Py_DECREF(seq);
    result = total(dbar, seqlen);
    free(dbar);
    return Py_BuildValue("d", result);
}

static PyMethodDef Methods[] = {
    {"add_one", py_add_one, METH_VARARGS},
    {"test_str", py_test_str, METH_VARARGS},
    {"total", totalDoubles, METH_VARARGS},
    {NULL, NULL}
};

static struct PyModuleDef cModule = {
    PyModuleDef_HEAD_INIT,
    "Test", /*module name*/
    "", /* module documentation, may be NULL */
    -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
    Methods
};

PyMODINIT_FUNC PyInit_Test(void){ return PyModule_Create(&cModule);}
setup.py
from distutils.core import setup, Extension
module1 = Extension('Test', sources = ['test.c'])
setup (name = 'Test',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])
run
python3 setup.py build
It will create a "Test.cpython-35m-x86_64-linux-gnu.so" in the build directory.
Make tt.py in the same directory, and run it by python3 tt.py
import Test as t
import array

x = 1
print(t.add_one(x))

print(t.test_str("123"))

array.array('i')
a = array.array('i', (0 for i in range(0, 10)))
a[0]=1
a[1]=2
print(t.total(a))
ref:
1. python的C擴展調用,使用原生的python-C-Api
2. 洞庭散人
3. Translating a Python Sequence into a C Array with the PySequence_Fast Protocol

沒有留言:

張貼留言