[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/cursospython/LibroPython/master/python_c_extension.rst [Back]  [Original]

Extensiones C de Python
===================

Una caracterstica muy interesante de la que disponemos en Python es tener un interfaz para interactuar con cdigo escrito en el lenguaje de programacin C. Existen varios mtodos, pero los que cubriremos en este captulo sern tres: ``ctypes``, ``SWIG`` y ``Python/C API``. Veremos las ventajas y desventajas de cada uno con algunos ejemplos de como pueden ser usados.

Pero antes tal vez te preguntes y para qu quiero yo usar cdigo C en Python? Pues bien, existen varias razones:

-   Si necesitas velocidad, C es mucho ms rpido que Python. En determinadas ocasiones puede ser del orden de decenas de veces ms rpido, por lo que si buscamos velocidad, esta puede ser una buena opcin.
-   Algunas libreras antiguas de C funcionan perfectamente, por lo que podra ser conveniente poder usarlas. Escribirlas otra vez en Python costara tiempo y dinero.
-   Tal vez quieras tener ciertos accesos a recursos muy a bajo nivel.
-   O tal vez simplemente porque quieres hacerlo.

Sabido esto, vamos a ver tres formas de usar cdigo C en Python.

CTypes
---------

El mdulo `ctypes `__ de Python es la forma ms sencilla de llamar a funciones escritas en C desde Python. Este mdulo nos proporciona tipos de datos compatibles con C, y funciones para cargar las libreras DLL de tal forma que puedan ser llamadas. La principal ventaja es que el cdigo escrito en C **no necesita ser modificado**.

**Ejemplo**

Tenemos un cdigo muy sencillo escrito en C que realiza la suma de dos nmeros. Lo guardamos en ``suma.c``.

.. code:: c

    //Cdigo C para sumar dos nmeros, enteros y floats.

    #include 

    int suma_int(int, int);
    float suma_float(float, float);

    int suma_int(int num1, int num2){
        return num1 + num2;
    }

    float suma_float(float num1, float num2){
        return num1 + num2;
    }

Ahora compilamos el cdigo C en un archivo ``.so`` (DLL para Windows). Esto generar el fichero ``adder.so``.

.. code:: bash

    #Para Linux
    $ gcc -shared -Wl,-soname,adder -o adder.so -fPIC suma.c

    #Para macOS
    $ gcc -shared -Wl,-install_name,adder.so -o adder.so -fPIC suma.c

Ahora en el cdigo Python, podemos hacer lo siguiente

.. code:: python

    from ctypes import *

    #Cargamos la librera
    adder = CDLL('./adder.so')

    #Realizamos la suma entera
    res_int = adder.suma_int(4,5)
    print "La suma de 4 y 5 es = " + str(res_int)

    #Realizamos la suma float
    a = c_float(5.5)
    b = c_float(4.1)

    suma_float = adder.suma_float
    suma_float.restype = c_float
    print "La suma de 5.5 y 4.1 es = ", str(suma_float(a, b))

Y tras ejecutarlo nos encontraramos con la siguiente salida:

::

    La suma de 4 y 5 es = 9
    La suma de 5.5 y 4.1 es = 9.60000038147

Vamos a explicar el ejemplo paso por paso. Por un lado tenemos el fichero de C, que tampoco necesita explicacin. Simplemente tenemos un par de funciones que suman dos valores, la primera enteros y la segunda float.

Por otro lado, en el fichero de Python importamos el mdulo ``ctypes``. Despus importamos la *shared library* que hemos creado usando la funcin ``CDLL``. Una vez hayamos hecho esto, las funciones definidas en la librera de C estarn disponibles en Python a travs de la variable ``adder``. Por lo tanto, podemos por ejemplo llamar a ``suma_int`` usando ``adder.suma_int()`` y pasando dos enteros como entrada. Esto producir que la funcin de C sea llamada con esos parmetros que hemos proporcionado y se nos devuelva la salida. Es importante notar que podemos usar por defectos los tipos enteros y cadenas.

Para otros tipos como booleanos o float, tenemos que especificarlo nosotros. Esto es por lo que cuando pasamos los parmetros a la otra funcin que hemos definido que usaba floats ``adder.suma_float()``, tenemos que especificar el tipo con ``c_float``. Como se puede ver esta forma es relativamente sencilla de implementar, pero tiene limitaciones. Por ejemplo, no sera posible manipular objetos en C.

SWIG
-------

Otra de las formas que existen para usar cdigo C desde Python, es usando SWIG, que viene de *Simplified Wrapper and Interface Generator*. En este mtodo, es necesario crear un nuevo interfaz (un fichero), que es usado como entrada para SWIG.

Es un mtodo no muy conocido ya que en la mayora de los casos es innecesariamente complejo. Sin embargo, es un mtodo bastante til cuando tenemos cierto cdigo en C/C++ y queremos usarlo en diferentes lenguajes (no slo en Python).

**Ejemplo** (De la `web de SWIG `__ )

Por un lado tenemos el cdigo en C guardado en ``ejemplo.c``, que tiene diferentes funciones y variables.

.. code:: c

     #include 
     double My_variable = 3.0;

     int fact(int n) {
         if (n >> import ejemplo
    >>> ejemplo.fact(5)
    120
    >>> ejemplo.my_mod(7,3)
    1
    >>> ejemplo.get_time()
    'Sun Feb 11 23:01:07 1996'
    >>>

Como podemos ver, el resultado que conseguimos con SWIG es el mismo, pero requiere de un poco ms des esfuerzo al tener que crear un fichero nuevo. Sin embargo tal vez merezca la pena si queremos compartir cdigo C con ms de un lenguaje, ya que este fichero de interfaz que hemos visto slo necesitara ser creado una vez.

Python/C API
---------------

Por ltimo, la `CPython API `__ es una de las opciones ms usadas, aunque no por su simplicidad. Esto se debe aunque a pesar de ser ms compleja que las anteriores vistas, nos permite manipular objetos de Python en C.

Este mtodo requiere que el cdigo C sea escrito de manera especfica para poder ser usado desde Python. Todos los objetos de Python se representan como un ``PyObject``, y la cabecera ``Python.h`` nos proporciona diferentes funciones para manipularlos. Por ejemplo, si el ``PyObject`` es un ``PyListType`` (es decir, una lista), podemos usar ``PyList_Size()`` para calcular su longitud. Sera el equivalente a usar ``len(list)`` en Python. En general, la mayora de las funciones de Python estn disponibles en ``Python.h``.

**Ejemplo**

Vamos a ver como escribir una extensin en C, que toma una lista y suma todos sus elementos. Vamos a asumir que todos los elementos son nmeros, como resulta evidente.

Empecemos viendo la forma en la que nos gustara poder usar la extensin de C que vamos a crear.

.. code:: python

    #A pesar de que parece un import normal, en realidad
    #importa la extensin en C que definiremos a continuacin.
    import sumaLista

    l = [1,2,3,4,5]
    print "La suma de la lista es " + str(l) + " = " +  str(sumaLista.add(l))

El ejemplo anterior podra parecer un fichero normal y corriente de Python, que importa y usa otro mdulo llamado ``sumaLista``. Sin embargo este mdulo no est escrito en Python sino en C. Esta es una de las ventajas principales, ya que en la parte de Python no nos tenemos que preocupar de aprender nada nuevo o usar funciones extra. Se nos abstrae la librera de C como si fuera un mdulo Python normal.

Lo siguiente es escribir el cdigo ``sumaLista`` que ser usado como hemos visto antes en Python. Puede parecer un poco complicado pero ya vers como no lo es tanto.

.. code:: c

    //Fichero: adder.c

    //Python.h proporciona funciones para manipular los objetos de Python
    #include 

    //Esta es la funcin que es llamada desde Python
    static PyObject* sumaLista_add(PyObject* self, PyObject* args){

      PyObject * listObj;

      //Los argumentos de entrada son proporcionados como una tupla, los parseamos para
      //obtener las variables. En este caso slo se trata de una lista, que ser 
      //referenciada por listObj.
      if (! PyArg_ParseTuple( args, "O", &listObj))
        return NULL;

      //Devuelve la longitud de la lista
      long length = PyList_Size(listObj);

      //Iteramos todos los elementos
      long i, sum =0;
      for(i = 0; i < length; i++){
        //Tomamos un elemento de la lista (tambin es un objeto Python)
        PyObject* temp = PyList_GetItem(listObj, i);
        //Sabemos que es un entero, por lo que lo convertimos a long
        long elem = PyInt_AsLong(temp);
        //Realizamos la suma y guardamos el resultado
        sum += elem;
      }

      //Devolvemos a Python otro objeto Python
      //Convertimos el long que tenamos en C a entero en Python
      return Py_BuildValue("i", sum);
    }

    //Este es un docstring (documentacin) de nuestra funcin suma.
    static char sumaLista_docs[] =
    "add( ): Suma todos los elementos de la lista\n";

    /* Esta tabla relaciona la siguiente informacin -
      , ,
      , 
    */
    static PyMethodDef sumaLista_funcs[] = {
      {"add", (PyCFunction)sumaLista_add, METH_VARARGS, sumaLista_docs},
      {NULL, NULL, 0, NULL}
    };

    /*
      sumaLista es el nombre del mdulo, y esto es la inicializacin.
      , , 

Web Proxy Viewer  |  New URL  |  Original Page