ctypes - początkujący


100

Mam zadanie "zawijania" biblioteki ac w klasę Pythona. Dokumentacja jest niesamowicie niejasna w tej sprawie. Wygląda na to, że oczekują, że tylko zaawansowani użytkownicy Pythona zaimplementują ctypes. Cóż, jestem początkującym w Pythonie i potrzebuję pomocy.

Jakaś pomoc krok po kroku byłaby cudowna.

Więc mam swoją bibliotekę C. Co ja robię? Jakie pliki mam gdzie umieścić? Jak zaimportować bibliotekę? Czytałem, że może istnieć sposób na „automatyczne zawijanie” do Pythona?

(Swoją drogą, zrobiłem samouczek ctypes na python.net i to nie działa. Oznacza to, że myślę, że zakładają, że powinienem być w stanie wykonać pozostałe kroki.

W rzeczywistości jest to błąd, który otrzymuję z ich kodem:

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

Naprawdę przydałaby mi się pomoc krok po kroku! Dzięki ~


10
Czy masz >>> plik importtest.py? Kiedy ludzie wysyłają kod, który ma >>> w każdym wierszu, oznacza to, że jest on uruchamiany w powłoce interaktywnej. Aby uruchomić go z pliku, usuń >>> (to jest 3> znaki i spacja), gdziekolwiek się pojawi.
Chinmay Kanchi,

4
Nie wpisuj >>>s. Są one drukowane przez interaktywną powłokę i powinny zostać pominięte w pliku źródłowym.
nmichaels

8
>>>w pliku .py! AUĆ! Nigdy wcześniej tego nie widziałem!
David Heffernan

3
Szczerze mówiąc, naucz się trochę Pythona (przynajmniej trochę), zanim zaczniesz bawić się ctypami. Nigdy nie znajdziesz samouczka na temat ctypów, który zakłada, że ​​nie znasz podstawowego języka Python.
Chinmay Kanchi

3
@spentak: jeśli prosisz o pomoc, podaj odpowiednie informacje. Pokaż nam przynajmniej ostatnią wersję kodu, o której mówisz. Co jest na przykład w „linii 3”?
Francesco

Odpowiedzi:


229

Oto szybki i brudny samouczek dotyczący ctypów.

Najpierw napisz swoją bibliotekę C. Oto prosty przykład Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

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

Teraz skompiluj go jako bibliotekę współdzieloną ( poprawkę mac można znaleźć tutaj ):

$ 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

Następnie napisz opakowanie używając ctypes:

testlibwrapper.py

import ctypes

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

Teraz wykonaj to:

$ python testlibwrapper.py

Powinieneś zobaczyć wynik

Hello world
$

Jeśli masz już na myśli bibliotekę, możesz pominąć nie-pythonową część samouczka. Upewnij się, że ctypes mogą znaleźć bibliotekę, umieszczając ją w /usr/liblub innym standardowym katalogu. Jeśli to zrobisz, nie musisz określać pełnej ścieżki podczas pisania opakowania. Jeśli zdecydujesz się tego nie robić, podczas dzwonienia musisz podać pełną ścieżkę do biblioteki ctypes.CDLL().

To nie jest miejsce na bardziej kompleksowy samouczek, ale jeśli poprosisz o pomoc w przypadku konkretnych problemów na tej stronie, jestem pewien, że społeczność Ci pomoże.

PS: Zakładam, że używasz Linuksa, ponieważ używałeś ctypes.CDLL('libc.so.6'). Jeśli korzystasz z innego systemu operacyjnego, rzeczy mogą się trochę zmienić (lub całkiem sporo).


1
@ Chinmay: Czy mogę mieć podobny kod dla systemu Windows i zamiast C, czy mógłbyś podać przykład wizualny w języku C ++? Mogę załadować swoją bibliotekę, ale nie mam dostępu do moich funkcji z pliku .dll. Zawsze mówi „funkcja 'xyz' nie została znaleziona”. Czy możesz zasugerować mi sposób na obejście tego? Twoje zdrowie.
Neophile

Nie wiem zbyt wiele o programowaniu w systemie Windows, ale wygląda na to, że Windows robi coś dziwnego, być może używa innej konwencji wywoływania? Być może możesz sprawdzić eksportowanie funkcji C ++ przy użyciu „extern C”?
Chinmay Kanchi,

Tak, zrobiłem to, ale na razie bez powodzenia.
Neophile,

6
Dzięki za łatwy do zrozumienia samouczek, który pokazuje podstawową funkcjonalność
ctype

1
szybkie i brudne są zawsze najlepszymi samouczkami
lurscher

55

Odpowiedź Chinmaya Kanchi jest doskonała, ale chciałem mieć przykład funkcji, która przekazuje i zwraca zmienne / tablice do kodu C ++. Pomyślałem, że umieściłbym to tutaj na wypadek, gdyby było przydatne dla innych.

Przekazywanie i zwracanie liczby całkowitej

Kod C ++ funkcji, która pobiera liczbę całkowitą i dodaje ją do zwracanej wartości,

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

Zapisany jako plik test.cpp, zanotuj wymagany zewnętrzny "C" (można go usunąć dla kodu C). Jest to kompilowane przy użyciu g ++, z argumentami podobnymi do odpowiedzi Chinmay Kanchi,

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

Kod Pythona wykorzystuje load_libraryod numpy.ctypeslibzałożenia ścieżki do biblioteki współdzielonej w tym samym katalogu co skrypt Pythona,

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)

To wypisuje 6 zgodnie z oczekiwaniami.

Przekazywanie i drukowanie tablicy

Możesz również przekazać tablice w następujący sposób, aby kod C wydrukował element tablicy,

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

który jest kompilowany jak poprzednio, a importowany w ten sam sposób. Dodatkowy kod Pythona do użycia tej funkcji wyglądałby wtedy:

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)

gdzie określamy tablicę, pierwszy argument do print_array, jako wskaźnik do tablicy Numpy wyrównanych, ciągłych 64-bitowych pływaków c_, a drugi argument jako liczbę całkowitą, która mówi kodowi C liczbę elementów w tablicy Numpy. To jest następnie drukowane przez kod C w następujący sposób,

1.4
2.6
3.0

5
To świetna dodatkowa odpowiedź - szkoda, że ​​nie może być dwóch zaznaczonych odpowiedzi :(
jtlz2

Nie jestem pewien, czy jest to zbyt oczywiste, ale w kodzie jest błąd. Brakuje import numpy as np. W przeciwnym razie nie jest w stanie znaleźć np.float64i innych rzeczy.
Ben

11

Po pierwsze: >>>kod, który widzisz w przykładach w Pythonie, jest sposobem na wskazanie, że jest to kod w Pythonie. Służy do oddzielania kodu Pythona od danych wyjściowych. Lubię to:

>>> 4+5
9

Tutaj widzimy, że wiersz zaczynający się od >>>jest kodem Pythona, a wynikiem jest 9. Dokładnie tak to wygląda, jeśli uruchomisz interpreter języka Python, dlatego właśnie tak się dzieje.

Nigdy nie wprowadzasz >>>części do .pypliku.

To rozwiązuje problem z błędem składni.

Po drugie, ctypes to tylko jeden z kilku sposobów pakowania bibliotek Pythona. Inne sposoby to SWIG , który sprawdzi twoją bibliotekę Pythona i wygeneruje moduł rozszerzenia Python C, który ujawnia C API. Innym sposobem jest użycie Cythona .

Wszystkie mają zalety i wady.

SWIG ujawni twoje C API tylko dla Pythona. Oznacza to, że nie dostajesz żadnych obiektów ani niczego, musisz to zrobić w osobnym pliku Pythona. Powszechne jest jednak posiadanie modułu o nazwie powiedzmy „wowza” i modułu SWIG o nazwie „_wowza”, który jest opakowaniem wokół C API. To miły i łatwy sposób na robienie rzeczy.

Cython generuje plik C-Extension. Ma tę zaletę, że cały kod Pythona, który piszesz, jest tworzony w C, więc obiekty, które piszesz, są również w C, co może poprawić wydajność. Ale musisz się nauczyć, jak łączy się z C, więc nauczenie się, jak go używać, wymaga trochę dodatkowej pracy.

ctypes mają tę zaletę, że nie ma kodu C do skompilowania, więc jest bardzo przyjemny w użyciu do pakowania standardowych bibliotek napisanych przez kogoś innego i już istnieje w wersjach binarnych dla Windows i OS X.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.