Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> python

Programación de Extensiones en Python con C: Crea Módulos Nativos Rápidos

Cualquier código escrito en lenguajes compilados como C, C++ o Java puede integrarse o importarse en otro script Python. Este código se considera una "extensión".

Un módulo de extensión Python no es más que una librería C normal. En Unix terminan en .so (shared object); en Windows en .dll (dynamically linked library).

Prerrequisitos para Escribir Extensiones

Necesitas los archivos de cabecera de Python:

Se asume buen conocimiento de C/C++ para escribir extensiones Python con C.

Primera Vista a una Extensión Python

Agrupa tu código en cuatro partes:

Archivo de Cabecera Python.h

Incluye Python.h en tu código C para acceder a la API interna de Python y enganchar tu módulo al intérprete.

Incluye Python.h antes que cualquier otra cabecera. Luego siguen las funciones a llamar desde Python.

Las Funciones C

Las firmas de implementación C siempre toman una de tres formas:

static PyObject *MyFunction(PyObject *self, PyObject *args);
static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw);
static PyObject *MyFunctionWithNoArgs(PyObject *self);

Todas retornan un objeto Python. No existe void en Python como en C. Si no quieres retornar valor, retorna el equivalente C de None usando macro Py_RETURN_NONE.

Nombres de funciones C pueden ser cualesquiera (son static y no se ven fuera). Convención: combinar nombre de módulo y función:

static PyObject *module_func(PyObject *self, PyObject *args) {
    /* Tu código aquí */
    Py_RETURN_NONE;
}

Tabla de Mapeo de Métodos

Array de estructuras PyMethodDef:

struct PyMethodDef {
    char *ml_name;
    PyCFunction ml_meth;
    int ml_flags;
    char *ml_doc;
};

Miembros:

La tabla termina con centinela {NULL, NULL, 0, NULL}.

Ejemplo

static PyMethodDef module_methods[] = {
    {"func", (PyCFunction)module_func, METH_NOARGS, NULL},
    {NULL, NULL, 0, NULL}
};

Función de Inicialización

Llamada por el intérprete Python al cargar el módulo. Debe llamarse init donde Module es el nombre del módulo.

Debe exportarse desde la librería. Python headers definen PyMODINIT_FUNC para las incantaciones apropiadas según entorno de compilación.

Estructura general:

PyMODINIT_FUNC initModule() {
    Py_InitModule3("func", module_methods, "docstring...");
}

Py_InitModule3 parámetros: func (función a exportar), module_methods (tabla de mapeo), docstring (comentario del módulo).

Ejemplo Completo: Hola Mundo

#include 

static PyObject* helloworld(PyObject* self) {
    return Py_BuildValue("s", "Hello, Python extensions!!");
}

static char helloworld_docs[] = "helloworld(): Any message you want to put here!!\n";

static PyMethodDef helloworld_funcs[] = {
    {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs},
    {NULL}
};

void inithelloworld(void) {
    Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!");
}

Py_BuildValue construye un valor Python. Guarda en hello.c. Veamos cómo compilar e instalar.

Compilación e Instalación de Extensiones

El paquete distutils facilita distribuir módulos Python (puros y extensiones) de forma estándar. Los módulos se distribuyen en código fuente y se compilan/instalan via script setup.py.

Para el módulo anterior, setup.py:

from distutils.core import setup, Extension
setup(name='helloworld', version='1.0',
      ext_modules=[Extension('helloworld', ['hello.c'])])

Comando para compilar, enlazar e instalar:

$ python setup.py install

En sistemas Unix, ejecuta como root para permisos en site-packages. En Windows no suele ser problema.

Importar Extensiones

Una vez instalada, importa y llama desde Python:

#!/usr/bin/python
import helloworld
print(helloworld.helloworld())

Resultado: Hello, Python extensions!!

Paso de Parámetros a Funciones

Para funciones que aceptan argumentos, usa firma con args:

static PyObject *module_func(PyObject *self, PyObject *args) {
    /* Parsear args y hacer algo interesante */
    Py_RETURN_NONE;
}

Entrada en tabla de métodos con METH_VARARGS:

static PyMethodDef module_methods[] = {
    {"func", module_func, METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL}
};

Usa API PyArg_ParseTuple para extraer argumentos del puntero PyObject:

static PyObject *module_func(PyObject *self, PyObject *args) {
    int i; double d; char *s;
    if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
        return NULL;
    }
    /* Hacer algo interesante */
    Py_RETURN_NONE;
}

Permite invocar: module.func(1, s="three", d=2.0), module.func(i=1, d=2.0, s="three"), etc.

Función PyArg_ParseTuple

Firma estándar:

int PyArg_ParseTuple(PyObject* tuple, char* format, ...)

Retorna 0 para error, ≠0 para éxito. tuple es el segundo argumento de la función C. format describe argumentos obligatorios y opcionales.

Códigos de Formato Comunes

CódigoTipo CSignificado
ccharString Python longitud 1 → C char
ddoubleFloat Python → C double
ffloatFloat Python → C float
iintInt Python → C int
llongInt Python → C long
Llong longInt Python → C long long
OPyObject*Referencia prestada no-NULL a argumento Python
schar*String Python sin nulls embebidos → C char*
s#char*+intCualquier string Python → dirección C y longitud
zchar*Como s, pero acepta None (pone C char* a NULL)
(...)según ...Secuencia Python tratada como un argumento por item
|Los siguientes argumentos son opcionales
:Fin de formato, seguido de nombre función para mensajes error
;Fin de formato, seguido de texto completo mensaje error

Retorno de Valores

Py_BuildValue toma string de formato como PyArg_ParseTuple, pero pasas valores reales (no direcciones).

Ejemplo: Función Suma

static PyObject *foo_add(PyObject *self, PyObject *args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;
    }
    return Py_BuildValue("i", a + b);
}

Equivalente Python: def add(a, b): return a + b

Retornar Múltiples Valores

static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;
    }
    return Py_BuildValue("ii", a + b, a - b);
}

En Python se captura como tupla/lista: def add_subtract(a, b): return (a + b, a - b)

Función Py_BuildValue

Firma estándar:

PyObject* Py_BuildValue(char* format, ...)

format describe el objeto Python a construir. Argumentos siguientes son valores C. Retorna nueva referencia PyObject*.

Códigos Comunes

CódigoTipo CSignificado
ccharC char → string Python longitud 1
ddoubleC double → float Python
ffloatC float → float Python
iintC int → int Python
llongC long → int Python
NPyObject*Pasa objeto Python y roba referencia
OPyObject*Pasa objeto Python e INCREF normal
O&convert+void*Conversión arbitraria
schar*C char* 0-terminado → string Python, o NULL → None
s#char*+intC char* y longitud → string Python, o NULL → None
uPy_UNICODE*C wide string null-terminado → Unicode Python, o NULL → None
u#Py_UNICODE*+intC wide string y longitud → Unicode Python, o NULL → None
zchar*Como s, acepta None
(...)según ...Construye tupla Python desde valores C
[...]según ...Construye lista Python desde valores C
{...}según ...Construye diccionario Python desde valores C (clave, valor alternados)

Código {...} construye diccionarios de número par de valores C, alternando claves y valores. Ejemplo: Py_BuildValue("{issi}", 23, "zig", "zag", 42) retorna diccionario como {23: 'zig', 'zag': 42}.

python

  1. Dominando la Programación Orientada a Objetos en Python: Clases, Herencia, Encapsulación y Polimorfismo
  2. Función print() en Python: Guía Práctica con Ejemplos
  3. Domine el Método strip() de Python: Guía Completa y Ejemplos Prácticos
  4. Domine str.count() en Python: Cómo Contar Caracteres y Subcadenas con Ejemplos
  5. Domine el Formateo de Strings en Python con format(): Ejemplos Claros
  6. Domine el Método String.find() en Python: Sintaxis, Ejemplos y Alternativas
  7. Dominando las funciones lambda en Python: ejemplos prácticos y mejores prácticas
  8. Función round() en Python explicada con ejemplos prácticos
  9. Dominando la función map() de Python: sintaxis, ejemplos y mejores prácticas
  10. Python timeit(): medición de tiempo de ejecución con ejemplos prácticos
  11. Counter de collections en Python: conteo eficiente, actualización y operaciones aritméticas