ctypes - Principiante


100

Ho il compito di "avvolgere" la libreria ac in una classe python. I documenti sono incredibilmente vaghi su questo argomento. Sembra che si aspettino che solo gli utenti avanzati di Python implementino i ctypes. Beh, sono un principiante in Python e ho bisogno di aiuto.

Un aiuto passo dopo passo sarebbe meraviglioso.

Quindi ho la mia libreria c. Cosa faccio? Quali file metto dove? Come si importa la libreria? Ho letto che potrebbe esserci un modo per "avvolgere automaticamente" in Python?

(A proposito, ho fatto il tutorial ctypes su python.net e non funziona. Significa che penso che stiano assumendo che dovrei essere in grado di completare il resto dei passaggi.

In effetti questo è l'errore che ottengo con il loro codice:

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

Potrei davvero usare un aiuto passo dopo passo su questo! Grazie ~


10
Hai >>> in importtest.py? Quando le persone >>> inseriscono il codice che ha su ogni riga, significa che viene eseguito nella shell interattiva. Per eseguirlo da un file, rimuovi >>> (cioè 3 segni e uno spazio) ovunque appaia.
Chinmay Kanchi

4
Non digitare la >>>s. Questi vengono stampati dalla shell interattiva e dovrebbero essere lasciati fuori dal file sorgente.
nmichaels

8
>>>nel file .py! AHIA! Mai visto prima!
David Heffernan

3
Onestamente, impara un po 'di Python (almeno un po') prima di iniziare a scherzare con i ctypes. Non troverai mai un tutorial sui ctypes che presume che tu non conosca Python di base.
Chinmay Kanchi

3
@spentak: se chiedi aiuto, fornisci informazioni adeguate. Mostraci almeno l'ultima versione del codice di cui parli. Cosa c'è sulla "linea 3", per esempio?
Francesco

Risposte:


228

Ecco un tutorial rapido e sporco sui ctypes.

Per prima cosa, scrivi la tua libreria C. Ecco un semplice esempio Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

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

Ora compilarlo come una libreria condivisa ( correzione per mac trovata qui ):

$ 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

Quindi, scrivi un wrapper usando ctypes:

testlibwrapper.py

import ctypes

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

Ora eseguilo:

$ python testlibwrapper.py

E dovresti vedere l'output

Hello world
$

Se hai già in mente una libreria, puoi saltare la parte non python del tutorial. Assicurati che ctypes possa trovare la libreria inserendola in /usr/libo in un'altra directory standard. In tal caso, non è necessario specificare il percorso completo durante la scrittura del wrapper. Se si sceglie di non farlo, è necessario fornire il percorso completo della libreria durante la chiamata ctypes.CDLL().

Non è il posto giusto per un tutorial più completo, ma se chiedi aiuto per problemi specifici su questo sito, sono sicuro che la community ti aiuterà.

PS: presumo che tu sia su Linux perché l'hai usato ctypes.CDLL('libc.so.6'). Se sei su un altro sistema operativo, le cose potrebbero cambiare un po '(o abbastanza).


1
@ Chinmay: Posso avere un codice simile per Windows e invece di C, potresti fornire un esempio visivo in C ++? Sono in grado di caricare la mia libreria ma non sono in grado di accedere alle mie funzioni dal file .dll. Dice sempre "funzione 'xyz' non trovata". Potresti suggerirmi un modo per aggirare questo? Saluti.
Neophile

Non so molto sullo sviluppo di Windows, ma sembra che Windows faccia qualcosa di traballante, forse usa una convenzione di chiamata diversa? Forse potresti cercare di esportare le tue funzioni C ++ usando "extern C"?
Chinmay Kanchi

Sì, l'ho fatto ma finora non ho avuto fortuna.
Neophile

6
Grazie per il tutorial facile da seguire che mostra le funzionalità di base di ctype
okysabeni

1
veloci e sporchi sono sempre i migliori tutorial
Lurscher

55

La risposta di Chinmay Kanchi è eccellente ma volevo un esempio di una funzione che passa e restituisce una variabile / array a un codice C ++. Ho pensato di includerlo qui nel caso fosse utile ad altri.

Passaggio e restituzione di un numero intero

Il codice C ++ per una funzione che accetta un numero intero e ne aggiunge uno al valore restituito,

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

Salvati come file test.cpp, annotare la "C" esterna richiesta (può essere rimossa per il codice C). Questo è compilato usando g ++, con argomenti simili alla risposta di Chinmay Kanchi,

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

Il codice Python utilizza load_librarydal numpy.ctypeslibpresupposto il percorso della libreria condivisa nella stessa directory dello script 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)

Questo stampa 6 come previsto.

Passaggio e stampa di un array

Puoi anche passare array come segue, per un codice C per stampare l'elemento di un array,

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

che viene compilato come prima e importato allo stesso modo. Il codice Python aggiuntivo per utilizzare questa funzione sarebbe quindi,

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)

dove specifichiamo l'array, il primo argomento a print_array, come un puntatore a un array Numpy di float allineati, c_contiguous a 64 bit e il secondo argomento come un numero intero che dice al codice C il numero di elementi nell'array Numpy. Questo poi stampato dal codice C come segue,

1.4
2.6
3.0

5
Questa è un'ottima risposta supplementare - peccato che non possano esserci due risposte spuntate :(
jtlz2

Non sono sicuro che sia troppo ovvio, ma c'è un errore nel codice. Manca import numpy as np. Altrimenti non è in grado di trovare np.float64e altre cose.
Ben

@ Ben, buon posto, aggiunto in
Ed Smith il

11

Primo: il >>>codice che vedi negli esempi Python è un modo per indicare che si tratta di codice Python. Viene utilizzato per separare il codice Python dall'output. Come questo:

>>> 4+5
9

Qui vediamo che la riga che inizia con >>>è il codice Python, e 9 è ciò che risulta. Questo è esattamente come appare se avvii un interprete Python, motivo per cui è fatto così.

Non inserisci mai la >>>parte in un .pyfile.

Questo si prende cura del tuo errore di sintassi.

In secondo luogo, ctypes è solo uno dei diversi modi per avvolgere le librerie Python. Altri modi sono SWIG , che esaminerà la tua libreria Python e genererà un modulo di estensione C Python che espone l'API C. Un altro modo è usare Cython .

Hanno tutti vantaggi e svantaggi.

SWIG esporrà la tua API C solo a Python. Ciò significa che non ottieni alcun oggetto o altro, dovrai creare un file Python separato per farlo. È comunque comune avere un modulo chiamato say "wowza" e un modulo SWIG chiamato "_wowza" che è il wrapper dell'API C. Questo è un modo semplice e piacevole di fare le cose.

Cython genera un file di estensione C. Ha il vantaggio che tutto il codice Python che scrivi viene trasformato in C, quindi anche gli oggetti che scrivi sono in C, il che può essere un miglioramento delle prestazioni. Ma dovrai imparare come si interfaccia con C quindi è un po 'di lavoro extra per imparare a usarlo.

ctypes ha il vantaggio che non c'è codice C da compilare, quindi è molto bello da usare per il wrapping di librerie standard scritte da qualcun altro ed esiste già nelle versioni binarie per Windows e OS X.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.