jump to navigation

Módulos en C para Python 06/10/2008

Posteado por Edorka en : C,Programacion,Python , trackback

Traducción de Anatomy of a Python C Module

Escribir módulos C en Python es relativamente fácil. La razón principal para hacer esto es incrementar el rendimiento de un código en Python. A continuación se demostrará como implementar la siguiente función de Python en C. Esta función puede encontrarse aquí:

    def fib2(n): # return Fibonacci series up to n
    """Return a list containing the Fibonacci series up to n."""
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)    # see below
        a, b = b, a+b
    return result

Cabe destacar que esta no es una función particularmente lenta -en realidad es bastante rápida- Simplemente tiene una serie de aspectos interesantes para implementar un modulo C en Python, como la creación de una lista de Python en C. El ejemplo de la creación de un modulo C para Python no es tan amplio como podría llegar a serlo, si todo va bien la implementación de la secuencia Fibonacci en C resultará un poco más explicativa.

Para empezar siempre se incluye Python.h:

#include Python.h;

Lo siguiente será crear la función fib. Primero definimos la función como un Python Object, al que se le pasaran argumentos:

static PyObject *
fib(PyObject *self, PyObject *args)
{

Después una vez en el cuerpo de la función inicializamos algunas variables:

   int a = 0, b = 1, c, n;

Entonces será cuando se realice el analisis de los parámetros proporcionados a la función. Para esto se utilizar  PyArg_ParseTuple. Puede encontrar mas documentación en Parsing arguments and building values, que dará una visión general sobre como analizar diferentes tipos de parámetros. De todos modos en este ejemplo solo se acepta un único valor entero. Si esto no funciona se devolverá NULL.

if (!PyArg_ParseTuple(args, "i", &n))
    return NULL;

Después se instanciará una nueva lista Python, utilizando PyList_New, que aceptará un número entero como longitud de la lista. Ya que se desconoce fual será la longitud se empezará con cero.

PyObject *list = PyList_New(0);

A continuación están las entrañas del verdadero cálculo. Se debe prestar atención a la sentencia PyList_Append(list, PyInt_FromLong(b));, ya que es donde añadimos un nuevo item a la lista. PyList_Append es analogo al metodo de Python list.append(). Se utiliza PyInt_FromLong para crear un objeto Python con un entero dentro del bucle.

while(b < n){
    PyList_Append(list, PyInt_FromLong(b));
    c = a+b;
    a = b;
    b = c;
}

Y entonces se devuelve la lista:

    return list;
}

Esto conforma las entrañas de la funcion, pero como integrar esto en Python como un módulo? Primero debe crearse un objeto PythonMethodDef con las funciones que quieren incorporarse al modulo. Ya que solo tenemos una función solo tenemos que añadir una definición, como esta:

PyMethodDef methods[] = {
    {"fib", fib, METH_VARARGS, "Returns a fibonacci sequence as a list"},
    {NULL, NULL, 0, NULL}
};

El último paso es inicializar el módulo. Para entender que ocurre en esta parte debe leerse esta página que contiene una explicación completa del proceso.

PyMODINIT_FUNC
initfib()
{
    (void) Py_InitModule("fib", methods);
}

Ahora que el modulo en C para Python esta completo, tiene que compilarse. La forma más fácil de hacerlo es utilizar el módulo distutils. Creamos setup.py como sigue:

from distutils.core import setup, Extension

setup(name = "Fib",
      version = "1.0",
      ext_modules = [Extension("fib", ["fib.c"])])

Con eso se informa a distutils que nuestro modulo esta ubicado en fib.c. Ahora se ejecuta:

$ python setup.py build
$ python setup.py install

Y ya está instalado, para utilizarlo debemos importar el módulo y utilizar la función:

import fib
fib.fib(123)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Una secuencia fibonacci es fácil de calcular, y hacerlo en C es un ejercicio para mostrar como se pueden implementar funciones y módulos en C. Aun así, y aunque sea facil de calcular, la versión en C es cuatro veces más rápida que su equivalente en Python. Este simple ejemplo debería mostrar como de fácil -y útil- es implementar extensiones en C

Hay mucho mas sobre este procedimiento en Extending and Embedding the Python Interpreter que recomiendo leer.

Comentarios»

1. isme1.mx - 04/12/2009

Gracias!!!!

Me fue de mucha ayuda tu ejemplo practico

2. Ivan - 10/12/2009

Hola Edorka,

mira aqi:
http://github.com/ivanistheone/arXivLDA/blob/master/project/pythonext-example/fib.c

en el code original fue uno “memory leak”.
Quando usamos Py_DECREF se fixo.

Ivan

3. Edorka - 11/12/2009

Muchas gracias por el aviso, a ver si saco un rato y modifico el código que he propuesto en el blog :)