ctypes - Principiante

100

Tengo la tarea de "envolver" la biblioteca de CA en una clase de Python. Los documentos son increíblemente vagos en este asunto. Parece que esperan que solo los usuarios avanzados de Python implementen ctypes. Bueno, soy un principiante en Python y necesito ayuda.

Alguna ayuda paso a paso sería maravillosa.

Entonces tengo mi biblioteca c. ¿Qué debo hacer? ¿Qué archivos pongo donde? ¿Cómo importo la biblioteca? Leí que podría haber una manera de "ajustar automáticamente" a Python.

(Por cierto, hice el tutorial de ctypes en python.net y no funciona. Lo que significa que estoy pensando que están asumiendo que debería poder completar el resto de los pasos.

De hecho, este es el error que obtengo con su código:

File "importtest.py", line 1
   >>> from ctypes import *
   SyntaxError: invalid syntax

¡Realmente me vendría bien un poco de ayuda paso a paso en esto! Gracias ~

Spentak
fuente
10
¿Tiene >>> en importtest.py? Cuando las personas publican un código que tiene >>> en cada línea, significa que se está ejecutando en el shell interactivo. Para ejecutarlo desde un archivo, elimine >>> (eso es 3> signos y un espacio) donde aparezca.
Chinmay Kanchi
4
No escriba la >>>s. Estos se imprimen mediante el shell interactivo y deben omitirse en su archivo fuente.
nmichaels
8
>>>en el archivo .py! ¡AY! ¡Nunca había visto eso antes!
David Heffernan
3
Honestamente, aprenda un poco de Python (al menos un poco) antes de empezar a jugar con ctypes. Que está no va a encontrar un tutorial sobre ctypes que se supone que no sabe Python básico.
Chinmay Kanchi
3
@spentak: si solicita ayuda, proporcione la información adecuada. Al menos muéstranos la última versión del código del que estás hablando. ¿Qué hay en la "línea 3", por ejemplo?
Francesco

Respuestas:

228

Aquí hay un tutorial de ctypes rápido y sucio.

Primero, escriba su biblioteca C. Aquí hay un ejemplo simple de Hola mundo:

testlib.c

#include <stdio.h>

void myprint(void);

void myprint()
{
    printf("hello world\n");
}

Ahora compílelo como una biblioteca compartida ( solución de mac que se encuentra aquí ):

$ gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

# or... for Mac OS X 
$ gcc -shared -Wl,-install_name,testlib.so -o testlib.so -fPIC testlib.c

Luego, escribe una envoltura usando ctypes:

testlibwrapper.py

import ctypes

testlib = ctypes.CDLL('/full/path/to/testlib.so')
testlib.myprint()

Ahora ejecútelo:

$ python testlibwrapper.py

Y deberías ver la salida

Hello world
$

Si ya tiene una biblioteca en mente, puede omitir la parte del tutorial que no es de Python. Asegúrese de que ctypes pueda encontrar la biblioteca colocándola en /usr/libotro directorio estándar. Si hace esto, no necesita especificar la ruta completa al escribir el contenedor. Si elige no hacer esto, debe proporcionar la ruta completa de la biblioteca al llamar ctypes.CDLL().

Este no es el lugar para un tutorial más completo, pero si solicita ayuda con problemas específicos en este sitio, estoy seguro de que la comunidad lo ayudará.

PD: Asumo que estás en Linux porque lo has usado ctypes.CDLL('libc.so.6'). Si tiene otro sistema operativo, las cosas pueden cambiar un poco (o bastante).

Chinmay Kanchi
fuente
1
@ Chinmay: ¿Puedo tener un código similar para Windows y en lugar de C, podría proporcionar un ejemplo visual de C ++? Puedo cargar mi biblioteca pero no puedo acceder a mis funciones desde el archivo .dll. Siempre dice "función 'xyz' no encontrada". ¿Podría sugerirme una forma de evitar esto? Salud.
Neophile
No sé mucho sobre el desarrollo de Windows, pero parece que Windows hace algo extraño, ¿quizás usa una convención de llamada diferente? ¿Quizás podría buscar exportar sus funciones de C ++ usando "extern C"?
Chinmay Kanchi
Sí, lo hice, pero hasta ahora no tuve suerte.
Neófilo
6
Gracias por el tutorial fácil de seguir que muestra la funcionalidad básica de ctype
okysabeni
1
rápido y sucio son siempre los mejores tutoriales
lurscher
55

La respuesta de Chinmay Kanchi es excelente, pero quería un ejemplo de una función que pasa y devuelve variables / matrices a un código C ++. Pensé que lo incluiría aquí en caso de que sea útil para otros.

Pasar y devolver un entero

El código C ++ para una función que toma un número entero y agrega uno al valor devuelto,

extern "C" int add_one(int i)
{
    return i+1;
}

Guardado como archivo test.cpp, tenga en cuenta el externo "C" requerido (esto se puede eliminar para el código C). Esto se compila usando g ++, con argumentos similares a la respuesta de Chinmay Kanchi,

g++ -shared -o testlib.so -fPIC test.cpp

Los usos de código Python load_librarydesde el numpy.ctypeslibsupuesto de la ruta a la biblioteca compartida en el mismo directorio que el script en Python,

import numpy.ctypeslib as ctl
import ctypes

libname = 'testlib.so'
libdir = './'
lib=ctl.load_library(libname, libdir)

py_add_one = lib.add_one
py_add_one.argtypes = [ctypes.c_int]
value = 5
results = py_add_one(value)
print(results)

Esto imprime 6 como se esperaba.

Pasar e imprimir una matriz

También puede pasar matrices de la siguiente manera, para que un código C imprima el elemento de una matriz,

extern "C" void print_array(double* array, int N)
{
    for (int i=0; i<N; i++) 
        cout << i << " " << array[i] << endl;
}

que se compila como antes y se importa de la misma manera. El código de Python adicional para usar esta función sería,

import numpy as np

py_print_array = lib.print_array
py_print_array.argtypes = [ctl.ndpointer(np.float64, 
                                         flags='aligned, c_contiguous'), 
                           ctypes.c_int]
A = np.array([1.4,2.6,3.0], dtype=np.float64)
py_print_array(A, 3)

donde especificamos la matriz, el primer argumento a print_array, como un puntero a una matriz Numpy de flotadores alineados, c_contiguous de 64 bits y el segundo argumento como un número entero que le dice al código C el número de elementos en la matriz Numpy. Esto luego impreso por el código C de la siguiente manera,

1.4
2.6
3.0
Ed Smith
fuente
5
Esta es una gran respuesta complementaria; lástima que no pueda haber dos respuestas marcadas :(
jtlz2
No estoy seguro de si es demasiado obvio, pero hay un error en el código. Falta import numpy as np. De lo contrario, no puede encontrar np.float64y las otras cosas.
Ben
@Ben, buen lugar, lo agregó
Ed Smith
11

En primer lugar: el >>>código que ve en los ejemplos de Python es una forma de indicar que es código Python. Se usa para separar el código Python de la salida. Me gusta esto:

>>> 4+5
9

Aquí vemos que la línea que comienza con >>>es el código de Python, y el resultado es 9. Así es exactamente como se ve si inicia un intérprete de Python, que es la razón por la que se hace así.

Nunca ingresa la >>>pieza en un .pyarchivo.

Eso se encarga de su error de sintaxis.

En segundo lugar, ctypes es solo una de las varias formas de empaquetar bibliotecas de Python. Otras formas son SWIG , que observará su biblioteca de Python y generará un módulo de extensión Python C que expone la API C. Otra forma es usar Cython .

Todos tienen ventajas e inconvenientes.

SWIG solo expondrá su API C a Python. Eso significa que no obtiene ningún objeto ni nada, tendrá que crear un archivo Python separado para hacerlo. Sin embargo, es común tener un módulo llamado say "wowza" y un módulo SWIG llamado "_wowza" que es el envoltorio de la API C. Esta es una forma agradable y sencilla de hacer las cosas.

Cython genera un archivo de extensión C. Tiene la ventaja de que todo el código Python que escribe se convierte en C, por lo que los objetos que escribe también están en C, lo que puede mejorar el rendimiento. Pero tendrá que aprender cómo interactúa con C, por lo que es un poco más de trabajo aprender a usarlo.

Los ctypes tienen la ventaja de que no hay código C para compilar, por lo que es muy bueno usarlo para empaquetar bibliotecas estándar escritas por otra persona, y ya existe en versiones binarias para Windows y OS X.

Lennart Regebro
fuente