Модуль расширения C с использованием Python

Опубликовано: 12 Апреля, 2022

Написание простого модуля расширения C напрямую с использованием API расширений Python и никаких других инструментов. Создать ручной модуль расширения для простого кода на языке C несложно. Но сначала мы должны убедиться, что код C имеет правильный файл заголовка.

Code #1 :

#include <math.h>
  
extern int gcd(int, int);
extern int in_mandel(double x0, double y0, int n);
extern int divide(int a, int b, int *remainder);
extern double avg(double *a, int n);
  
typedef struct Point
{
    double x, y;
} Point;
  
extern double distance(Point *p1, Point *p2);


Заголовок соответствует библиотеке, которая была скомпилирована отдельно. Приведенный ниже код иллюстрирует основы написания функций расширения, исходя из этого предположения.

Code #2:

# include "Python.h"
# include "sample.h"
  
/* int gcd(int, int) */
static PyObject * py_gcd(PyObject * self, PyObject * args)
{
    int x, y, result;
    if (! PyArg_ParseTuple(args, "ii", &x, &y))
    {
        return NULL;
    }
    result = gcd(x, y);
    return Py_BuildValue("i", result);
}
  
/* int divide(int, int, int *) */
static PyObject * py_divide(PyObject * self, PyObject * args)
{
    int a, b, quotient, remainder;
    if (! PyArg_ParseTuple(args, "ii", &a, &b))
    {
        return NULL;
    }
    quotient = divide(a, b, &remainder);
    return Py_BuildValue("(ii)", quotient, remainder);
}


Код # 3: таблица и структура методов модуля

/* Module method table */
static PyMethodDef SampleMethods[] =
{
    {"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
    {"divide", py_divide, METH_VARARGS, "Integer division"},
    { NULL, NULL, 0, NULL}
};
  
/* Module structure */
static struct PyModuleDef samplemodule =
{
    PyModuleDef_HEAD_INIT,
    "sample", /* name of module */
    "A sample module", /* Doc string (may be NULL) */
    -1, /* Size of per-interpreter state or -1 */
    SampleMethods /* Method table */
};
  
/* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void)
{
    return PyModule_Create(&samplemodule);
}

Code #4: Creating a setup.py python file for building the extension module.

# setup.py
from distutils.core import setup, Extension
  
setup(name="sample",
    ext_modules=[
            Extension("sample",
                    ["pysample.c"],
                    include_dirs = ["/some/dir"],
                    define_macros = [("FOO","1")],
                    undef_macros = ["BAR"],
                    library_dirs = ["/usr/local/lib"],
                    libraries = ["sample"]
                    )
            ]
)

Code #5: Now simply use python3 buildlib.py build_ext --inplace, to build the resulting library.

bash% python3 setup.py build_ext --inplace
running build_ext
building "sample" extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
  -I/usr/local/include/python3.3m -c pysample.c
  -o build/temp.macosx-10.6-x86_64-3.3/pysample.o
gcc -bundle -undefined dynamic_lookup
build/temp.macosx-10.6-x86_64-3.3/pysample.o 
  -L/usr/local/lib -lsample -o sample.so
bash %

The above code will create a shared library called sample.so.
 
Code #6 :

import sample
  
print ("gcd = ", sample.gcd(35, 42))
  
print (" distance : ", sample.divide(42, 8))

Выход :

gcd = 7

расстояние = (5, 2)


«Расширение и встраивание интерпретатора Python» - это документация Python, с которой можно ознакомиться, прежде чем пытаться создать какое-либо расширение, написанное от руки.

В модулях расширения функции могут быть написаны, как показано во фрагменте кода ниже.

Code #4 :

static PyObject *py_func(PyObject *self, PyObject *args)
{
    ...
}

 

  • PyObject – C data type that represents any Python object. At a very high level, an extension function is a C function that receives a tuple of Python objects (in PyObject *args) and returns a new Python object as a result. The self argument to the function is unused for simple extension functions, but comes into play should you want to define new classes or object types in C.
  • The PyArg_ParseTuple() function is used to convert values from Python to a C representation. As input, it takes a format string that indicates the required values, such as “i” for integer and “d” for double, as well as the addresses of C variables in which to place the converted results.
  • Py_BuildValue() function is used to create Python objects from C data types. It also accepts a format code to indicate the desired type. In the extension functions, it is used to return results back to Python. One feature of Py_BuildValue() is that it can build more complicated kinds of objects, such as tuples and dictionaries.

Внимание компьютерщик! Укрепите свои основы с помощью базового курса программирования Python и изучите основы.

Для начала подготовьтесь к собеседованию. Расширьте свои концепции структур данных с помощью курса Python DS. А чтобы начать свое путешествие по машинному обучению, присоединяйтесь к курсу Машинное обучение - базовый уровень.