Я хочу назвать библиотеку C из приложения Python. Я не хочу обертывать целый API, только функции и типы данных, которые относятся к моему случаю. Поскольку я вижу его, у меня есть три варианта:
ctypes
общаться с внешней библиотекой.Я не уверен или 2) или 3) являюсь лучшим выбором. Преимущество 3) является этим ctypes
часть стандартной библиотеки, и получающимся кодом был бы чистый Python – хотя я не уверен, насколько большой, которые способствуют на самом деле.
Есть ли больше преимуществ / недостатки с любым выбором? Какой подход Вы рекомендуете?
Править: Спасибо за все Ваши ответы они предоставляют хороший ресурс любому надеющемуся делать что-то подобное. Решение, конечно, состоит в том, чтобы все еще быть принято для единственного случая — нет никого, "Это - правильная вещь" вид ответа. Для моего собственного случая я, вероятно, пойду с ctypes, но я также надеюсь испытать Cython в некотором другом проекте.
С тем, чтобы там быть никаким единственным истинным ответом принимая каждый несколько произволен; я выбрал ответ FogleBird's, поскольку он обеспечивает некоторое хорошее понимание ctypes, и это в настоящее время также - проголосовавший самым высоким образом ответ. Однако я предлагаю прочитать все ответы для получения хорошего обзора.
Еще раз спасибо.
ctypes
- лучший вариант для быстрого выполнения задачи, и работать с ним приятно, пока вы все еще пишете Python!
Недавно я упаковал FTDI драйвер для связи с USB-чипом с использованием ctypes, и это было здорово. Я сделал все это менее чем за один рабочий день. (Я реализовал только те функции, которые нам были нужны, около 15 функций.)
Ранее мы использовали сторонний модуль, PyUSB , для той же цели. PyUSB - это фактический модуль расширения C / Python. Но PyUSB не выпускал GIL при блокировании чтения / записи, что вызывало у нас проблемы. Поэтому я написал наш собственный модуль, используя ctypes, который освобождает GIL при вызове собственных функций.
Следует отметить, что ctypes победили. я не знаю о константах #define
и прочем в используемой вами библиотеке, только о функциях, поэтому вам придется переопределить эти константы в собственном коде.
Вот пример того, как код закончил поиск (много вырезано, просто пытаюсь показать вам суть):
from ctypes import *
d2xx = WinDLL('ftd2xx')
OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3
...
def openEx(serial):
serial = create_string_buffer(serial)
handle = c_int()
if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
return Handle(handle.value)
raise D2XXException
class Handle(object):
def __init__(self, handle):
self.handle = handle
...
def read(self, bytes):
buffer = create_string_buffer(bytes)
count = c_int()
if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
return buffer.raw[:count.value]
raise D2XXException
def write(self, data):
buffer = create_string_buffer(data)
count = c_int()
bytes = len(data)
if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
return count.value
raise D2XXException
Кто-то провел несколько тестов для различных вариантов.
Я мог бы быть более нерешительным, если бы мне пришлось заключить библиотека C ++ с множеством классов / шаблонов / и т. д. Но ctypes хорошо работает со структурами и может даже обратный вызов в Python.
from ctypes import *
d2xx = WinDLL('ftd2xx')
OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3
...
def openEx(serial):
serial = create_string_buffer(serial)
handle = c_int()
if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
return Handle(handle.value)
raise D2XXException
class Handle(object):
def __init__(self, handle):
self.handle = handle
...
def read(self, bytes):
buffer = create_string_buffer(bytes)
count = c_int()
if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
return buffer.raw[:count.value]
raise D2XXException
def write(self, data):
buffer = create_string_buffer(data)
count = c_int()
bytes = len(data)
if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
return count.value
raise D2XXException
Кто-то провел несколько тестов для различных опций.
Я мог бы быть более нерешительным, если бы мне пришлось обернуть библиотеку C ++ большим количеством классов / шаблонов / и т. Д. Но ctypes хорошо работает со структурами и может даже обратный вызов в Python.
from ctypes import *
d2xx = WinDLL('ftd2xx')
OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3
...
def openEx(serial):
serial = create_string_buffer(serial)
handle = c_int()
if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
return Handle(handle.value)
raise D2XXException
class Handle(object):
def __init__(self, handle):
self.handle = handle
...
def read(self, bytes):
buffer = create_string_buffer(bytes)
count = c_int()
if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
return buffer.raw[:count.value]
raise D2XXException
def write(self, data):
buffer = create_string_buffer(data)
count = c_int()
bytes = len(data)
if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
return count.value
raise D2XXException
Кто-то провел несколько тестов для различных опций.
Я мог бы быть более нерешительным, если бы мне пришлось обернуть библиотеку C ++ большим количеством классов / шаблонов / и т. Д. Но ctypes хорошо работает со структурами и может даже обратный вызов в Python.
Лично я бы написал модуль расширения на C. Не пугайтесь расширений Python C - их совсем несложно написать. Документация очень понятна и полезна. Когда я впервые написал расширение C на Python, я думаю, что мне потребовалось около часа, чтобы понять, как его написать - совсем немного времени.
Когда вызывающий и многопоточный метод совместно используют переменную, у вас уже есть доступ к ней - после завершения потока вы просто проверяете someValue
.
Конечно , вы должны знать, когда будет завершен многопоточный метод, чтобы это было полезно. Внизу есть два способа сделать это:
Отправить обратный вызов в многопоточный метод, который он может выполнить по завершении. Вы можете передать свой метод обратного вызова someValue
. Вы можете использовать эту технику, если вам все равно, когда выполняется обратный вызов.
Используйте WaitHandle
какого-либо типа (или Thread.Join
). Они сообщают вам, когда ресурс готов или событие завершилось. Этот метод полезен, если вы хотите запустить поток, сделать что-нибудь еще, затем дождитесь завершения потока, прежде чем продолжить.
Cython - довольно крутой инструмент, который стоит изучить, и он удивительно близок к синтаксису Python. Если вы выполняете какие-либо научные вычисления с помощью Numpy, то Cython - это то, что вам нужно, потому что он интегрируется с Numpy для быстрых матричных операций.
Cython - это надмножество языка Python. Вы можете использовать любой действительный файл Python, и он выдаст корректную программу на C. В этом случае Cython просто сопоставляет вызовы Python с базовым API CPython. Это приводит к ускорению, возможно, на 50%, потому что ваш код больше не интерпретируется.
Чтобы получить некоторую оптимизацию, вы должны начать сообщать Cython дополнительные факты о вашем коде, такие как объявления типов. Если вы расскажете достаточно, это может свести код к чистому C. То есть цикл for в Python становится циклом for в C. Здесь вы увидите огромный прирост скорости. Здесь вы также можете ссылаться на внешние программы C.
Использовать код Cython также невероятно просто. Я думал, что в руководстве это звучит сложно. Вы буквально просто делаете:
$ cython mymodule.pyx
$ gcc [some arguments here] mymodule.c -o mymodule.so
, а затем можете импортировать mymodule
в свой код Python и полностью забыть, что он компилируется до C.
В любом случае, потому что Cython так легко настроить и запустить Используя, я предлагаю попробовать его, чтобы узнать, подходит ли он вашим потребностям. Это не будет пустой тратой, если окажется, что это не тот инструмент, который вы ищете.
Я предлагаю попробовать, чтобы узнать, подходит ли он вашим потребностям. Это не будет пустой тратой, если окажется, что это не тот инструмент, который вы ищете. Я предлагаю попробовать, чтобы узнать, подходит ли он вашим потребностям. Это не будет пустой тратой, если окажется, что это не тот инструмент, который вы ищете. Если у вас уже есть библиотека с определенным API, я думаю, что ctypes
- лучший вариант, так как вам нужно только немного инициализировать, а затем более или менее вызвать библиотеку так, как вы привыкли.
Я думаю, Cython или создание модуля расширения на C (что не очень сложно) более полезны, когда вам нужен новый код, например, вызов этой библиотеки и выполнение некоторых сложных, временных выполнение задач, а затем передача результата в Python.
Другой подход, для простых программ, заключается в непосредственном выполнении другого процесса (скомпилированном извне), вывод результата на стандартный вывод и вызов его с помощью модуля подпроцесса. Иногда это самый простой подход.
Например, если вы создаете консольную программу на C, которая работает более или менее таким же образом
$miCcode 10
Result: 12345678
Вы можете вызвать ее из Python
>>> import subprocess
>>> p = subprocess.Popen(['miCcode', '10'], shell=True, stdout=subprocess.PIPE)
>>> std_out, std_err = p.communicate()
>>> print std_out
Result: 12345678
С небольшим форматированием строки, Вы можете воспринимать результат как хотите. Вы также можете захватить стандартный вывод ошибок, так что он довольно гибкий.
ctypes - это здорово, когда у вас уже есть скомпилированный библиотечный блок (например, библиотеки OS). Однако, накладные расходы по вызову очень велики, так что если вы будете делать много вызовов в библиотеку, и вы все равно будете писать C-код (или, по крайней мере, компилировать его), то я бы сказал, что стоит попробовать cython. Это не так уж и много, и использовать полученный pyd-файл будет намного быстрее и более питоновым.
Я лично склонен использовать cython для быстрого ускорения питонового кода (циклы и сравнения целых чисел - это две области, где особенно сияет cython), и когда будет задействован еще какой-нибудь код/обертка других библиотек, я обращусь к Boost.Python. Boost.Python может быть привередливым в настройке, но как только он работает, он делает обёртывание Си/Си++ кода простым.
cython также хорош в обёртывании numpy (чему я научился в работе SciPy 2009), но я не использовал numpy, так что я не могу это прокомментировать.