ctypes - Начинающий

100

У меня есть задача «обернуть» библиотеку ac в класс python. Документы по этому поводу очень расплывчаты. Похоже, они ожидают, что только опытные пользователи Python будут реализовывать ctypes. Что ж, я новичок в Python и мне нужна помощь.

Некоторая пошаговая помощь была бы замечательной.

Итак, у меня есть библиотека c. Что мне делать? Какие файлы куда класть? Как мне импортировать библиотеку? Я читал, что может быть способ «автоматического переноса» на Python?

(Кстати, я сделал учебник ctypes на python.net, и он не работает. Это означает, что я думаю, что они предполагают, что я смогу выполнить остальные шаги.

На самом деле это ошибка, которую я получаю с их кодом:

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

Мне действительно нужна пошаговая помощь по этому поводу! Спасибо ~

потраченный
источник
10
У вас есть >>> в importtest.py? Когда люди публикуют код, который есть >>> в каждой строке, это означает, что он выполняется в интерактивной оболочке. Чтобы запустить его из файла, удалите >>> (это 3 знака> и пробел) везде, где он появляется.
Чинмай Канчи
4
Не набирайте >>>s. Они печатаются интерактивной оболочкой и не должны входить в исходный файл.
nmichaels
8
>>>в файле .py! ОЙ! Никогда раньше такого не видел!
Дэвид Хеффернан
3
Честно говоря, изучите немного Python (хотя бы немного), прежде чем начинать возиться с ctypes. Вы никогда не найдете учебник по ctypes, предполагающий, что вы не знаете базового Python.
Чинмай Канчи
3
@spentak: если вы просите о помощи, предоставьте соответствующую информацию. По крайней мере, покажите нам последнюю версию кода, о которой вы говорите. Что, например, находится в "строке 3"?
Francesco

Ответы:

229

Вот краткое и грязное руководство по ctypes.

Сначала напишите свою библиотеку C. Вот простой пример Hello world:

testlib.c

#include <stdio.h>

void myprint(void);

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

Теперь скомпилируйте его как общую библиотеку ( исправление для Mac можно найти здесь ):

$ 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

Затем напишите оболочку, используя ctypes:

testlibwrapper.py

import ctypes

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

Теперь выполните:

$ python testlibwrapper.py

И вы должны увидеть результат

Hello world
$

Если у вас уже есть библиотека, вы можете пропустить часть учебника, не относящуюся к Python. Убедитесь, что ctypes может найти библиотеку, поместив ее в /usr/libдругой стандартный каталог. Если вы это сделаете, вам не нужно указывать полный путь при написании оболочки. Если вы решите не делать этого, вы должны указать полный путь к библиотеке при вызовеctypes.CDLL() .

Здесь не место для более подробного руководства, но если вы попросите помощи с конкретными проблемами на этом сайте, я уверен, что сообщество вам поможет.

PS: Я предполагаю, что вы используете Linux, потому что вы использовали ctypes.CDLL('libc.so.6'). Если вы используете другую ОС, все может немного измениться (или довольно сильно).

Чинмай Канчи
источник
1
@ Chinmay: Могу ли я иметь аналогичный код для Windows и вместо C, не могли бы вы предоставить визуальный пример C ++? Я могу загрузить свою библиотеку, но не могу получить доступ к своим функциям из файла .dll. Всегда написано «функция 'xyz' не найдена». Не могли бы вы предложить мне способ обойти это? Ура.
Neophile
Я не очень разбираюсь в разработке для Windows, но похоже, что Windows делает что-то странное, возможно, она использует другое соглашение о вызовах? Возможно, вы могли бы поискать экспорт ваших функций C ++ с помощью "extern C"?
Chinmay Kanchi
Да, я сделал это, но пока безуспешно.
Neophile
6
Спасибо за простой в использовании учебник, в котором показаны основные функции ctype
okysabeni
1
быстрые и грязные - всегда лучшие уроки
lurscher
55

Ответ Чинмая Канчи превосходен, но мне нужен пример функции, которая передает и возвращает переменные / массивы в код C ++. Я хотел бы включить его здесь, если он будет полезен другим.

Передача и возврат целого числа

Код C ++ для функции, которая принимает целое число и добавляет единицу к возвращаемому значению,

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

Сохраняется как файл test.cpp, обратите внимание на обязательный внешний "C" (его можно удалить для кода C). Это скомпилировано с использованием g ++ с аргументами, аналогичными ответу Чинмая Канчи,

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

Кодовый Python использует load_libraryот numpy.ctypeslibпредполагающего пути к разделяемой библиотеке в том же каталоге, что и сценарий 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)

Это напечатает 6, как и ожидалось.

Передача и печать массива

Вы также можете передавать массивы следующим образом, чтобы код C печатал элемент массива,

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

который компилируется, как и раньше, и импортируется таким же образом. Дополнительный код Python для использования этой функции будет таким:

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)

где мы указываем массив, первый аргумент print_arrayв качестве указателя на массив Numpy выровненных, c_contiguous 64-битных чисел с плавающей запятой, а второй аргумент как целое число, которое сообщает коду C количество элементов в массиве Numpy. Затем это печатается кодом C следующим образом:

1.4
2.6
3.0
Эд Смит
источник
5
Это отличный дополнительный ответ - жаль, что не может быть двух отмеченных ответов :(
jtlz2
Не уверен, что это слишком очевидно, но в коде есть ошибка. Его нет import numpy as np. Иначе не удастся найти np.float64и прочее.
Бен
11

Во-первых: >>>код, который вы видите в примерах Python, - это способ указать, что это код Python. Он используется для отделения кода Python от вывода. Как это:

>>> 4+5
9

Здесь мы видим, что строка, с которой начинается, >>>является кодом Python, а результатом является цифра 9. Именно так это выглядит, если запустить интерпретатор Python, вот почему это делается именно так.

Вы никогда не вводите >>>деталь в .pyфайл.

Это позаботится о вашей синтаксической ошибке.

Во-вторых, ctypes - это всего лишь один из нескольких способов обертывания библиотек Python. Другими способами являются SWIG , который просматривает вашу библиотеку Python и генерирует модуль расширения Python C, который предоставляет C API. Другой способ - использовать Cython .

У всех есть свои преимущества и недостатки.

SWIG предоставит Python только C API. Это означает, что вы не получаете никаких объектов или чего-то еще, вам придется создать для этого отдельный файл Python. Однако часто используется модуль с именем say "wowza" и модуль SWIG с именем "_wowza", который является оболочкой для C API. Это приятный и простой способ делать что-то.

Cython генерирует файл C-Extension. Его преимущество заключается в том, что весь код Python, который вы пишете, сделан на C, поэтому объекты, которые вы пишете, также находятся на C, что может улучшить производительность. Но вам нужно будет узнать, как он взаимодействует с C, поэтому вам придется потрудиться, чтобы научиться его использовать.

Преимущество ctypes состоит в том, что для компиляции нет C-кода, поэтому его очень приятно использовать для упаковки стандартных библиотек, написанных кем-то другим, и уже существует в двоичных версиях для Windows и OS X.

Леннарт Регебро
источник