Я хочу вызвать библиотеку C из приложения Python. Я не хочу оборачивать весь API, только функции и типы данных, которые имеют отношение к моему случаю. На мой взгляд, у меня есть три варианта:
- Создайте действительный модуль расширения на C. Вероятно, это излишне, и я также хотел бы избежать накладных расходов на обучение написанию расширений.
- Используйте Cython для предоставления соответствующих частей из библиотеки C в Python.
- Делайте все это в Python, используя
ctypes
для связи с внешней библиотекой.
Я не уверен, что 2) или 3) - лучший выбор. Преимущество 3) состоит в том, что он ctypes
является частью стандартной библиотеки, и в результате получается чистый Python, хотя я не уверен, насколько велико это преимущество на самом деле.
Есть ли больше преимуществ / недостатков с любым выбором? Какой подход вы рекомендуете?
Изменить: Спасибо за все ваши ответы, они предоставляют хороший ресурс для тех, кто хочет сделать что-то подобное. Решение, конечно, все еще должно быть принято для единственного случая - нет единственного ответа «Это правильно». Для моего собственного случая я, вероятно, пойду с ctypes, но я также с нетерпением жду возможности попробовать Cython в каком-то другом проекте.
Поскольку единого истинного ответа не существует, принять его несколько произвольно; Я выбрал ответ FogleBird, так как он дает хорошее представление о ctypes, и в настоящее время он также является самым популярным. Тем не менее, я предлагаю прочитать все ответы, чтобы получить хороший обзор.
Еще раз спасибо.
Ответы:
ctypes
это ваш лучший выбор для быстрого выполнения работы, и с вами приятно работать, поскольку вы все еще пишете на Python!Недавно я обернул драйвер FTDI для связи с USB-чипом с помощью ctypes, и это было здорово. Я сделал все это и работал менее чем за один рабочий день. (Я реализовал только те функции, которые нам нужны, около 15 функций).
Ранее мы использовали сторонний модуль PyUSB для той же цели. PyUSB - это модуль расширения C / Python. Но PyUSB не выпускал GIL при блокировке чтения / записи, что вызывало у нас проблемы. Поэтому я написал наш собственный модуль с использованием ctypes, который высвобождает GIL при вызове нативных функций.
Следует отметить, что ctypes не будет знать о
#define
константах и материалах в используемой вами библиотеке, а только о функциях, поэтому вам придется переопределять эти константы в своем собственном коде.Вот пример того, как код в конечном итоге выглядел (многие вырезали, просто пытаясь показать суть):
Кто-то сделал несколько тестов на различные варианты.
Я мог бы быть более нерешительным, если бы мне пришлось обернуть библиотеку C ++ большим количеством классов / шаблонов / и т. Д. Но ctypes хорошо работает со структурами и может даже вызвать обратный вызов в Python.
источник
ctypes
(дляpyinotify
), но я хотел бы понять проблему более тщательно.One thing to note is that ctypes won't know about #define constants and stuff in the library you're using, only the functions, so you'll have to redefine those constants in your own code.
Итак, я должен определить константы, которые есть вwinioctl.h
....ctypes
намного медленнее, чем c-extension, поскольку узким местом является интерфейс от Python к CПредупреждение: мнение разработчика ядра Cython впереди.
Я почти всегда рекомендую Cython поверх ctypes. Причина в том, что у него гораздо более плавный путь обновления. Если вы используете ctypes, поначалу многие вещи будут простыми, и, конечно, здорово написать свой код FFI на простом Python, без компиляции, сборок зависимостей и всего такого. Тем не менее, в какой-то момент вы почти наверняка обнаружите, что вам приходится часто заходить в свою библиотеку C, либо в цикле, либо в более длинных сериях взаимозависимых вызовов, и вы хотели бы ускорить это. В этот момент вы заметите, что вы не можете сделать это с помощью ctypes. Или, когда вам нужны функции обратного вызова и вы обнаружите, что ваш код обратного вызова Python становится узким местом, вы хотели бы ускорить его и / или переместить также в C. Опять же, вы не можете сделать это с ctypes.
С помощью Cython, OTOH, вы можете совершенно свободно делать код переноса и вызова таким тонким или толстым, как вам нужно. Вы можете начать с простых вызовов в ваш C-код из обычного Python-кода, и Cython переведет их в нативные C-вызовы без каких-либо дополнительных затрат на вызовы и с чрезвычайно низкими издержками преобразования для параметров Python. Когда вы заметите, что вам нужно еще больше производительности в какой-то момент, когда вы делаете слишком много дорогих вызовов в вашу библиотеку C, вы можете начать аннотировать окружающий код Python статическими типами и позволить Cython оптимизировать его непосредственно для C для вас. Или вы можете начать переписывать части своего C-кода на Cython, чтобы избежать вызовов, а также специализировать и сжимать ваши циклы алгоритмически. И если вам нужен быстрый обратный звонок, просто напишите функцию с соответствующей подписью и передайте ее в реестр обратных вызовов C напрямую. Опять же, никаких накладных расходов, и это дает вам простую производительность C-вызовов. И в гораздо менее вероятном случае, когда вы действительно не можете получить свой код достаточно быстро в Cython, вы все равно можете рассмотреть возможность переписать действительно важные его части в C (или C ++ или Fortran) и вызывать его из своего кода Cython естественным и естественным образом. Но тогда это действительно становится последним средством вместо единственного варианта.
Итак, ctypes хорош для простых вещей и быстрого запуска. Однако, как только дела начнут расти, вы, скорее всего, придете к тому, что заметите, что лучше использовать Cython с самого начала.
источник
__radd__
). Это особенно раздражает, когда вы планируете, чтобы ваш класс взаимодействовал со встроенными типами (например,int
иfloat
). Кроме того, магические методы в Cython просто немного глючат в целом.Cython - довольно интересный инструмент, который стоит изучить, и он удивительно близок к синтаксису Python. Если вы выполняете какие-либо научные вычисления с Numpy, то Cython - это то, что нужно, потому что он интегрируется с Numpy для быстрых операций с матрицами.
Cython - это расширенная версия языка Python. Вы можете добавить в него любой допустимый файл Python, и он выплюнет действительную программу на Си. В этом случае Cython просто сопоставит вызовы Python с базовым API CPython. Это может привести к ускорению на 50%, поскольку ваш код больше не интерпретируется.
Чтобы получить некоторые оптимизации, вы должны начать рассказывать Cython дополнительные факты о вашем коде, такие как объявления типов. Если вы скажете это достаточно, он может свести код до чистого C. То есть цикл for в Python становится циклом for в C. Здесь вы увидите значительное увеличение скорости. Вы также можете ссылаться на внешние программы на C здесь.
Использование кода Cython также невероятно просто. Я думал, что руководство делает это звучит сложно. Вы буквально просто делаете:
а затем вы можете
import mymodule
в своем коде Python и полностью забыть, что он компилируется до C.В любом случае, поскольку Cython очень прост в настройке и использовании, я советую попробовать, чтобы он соответствовал вашим потребностям. Это не будет пустой тратой, если окажется, что это не тот инструмент, который вы ищете.
источник
mymod.pyx
модуль, а затем сделайте,import pyximport; pyximport.install(); import mymod
и компиляция происходит за кулисами.runcython mymodule.pyx
. И в отличие от pyximport вы можете использовать его для более сложных задач связывания. Единственное предостережение в том, что я тот, кто написал для него 20 строк bash и может быть предвзятым.Для вызова библиотеки C из приложения Python также существует cffi, который является новой альтернативой для ctypes . Это приносит свежий взгляд на FFI:
источник
Я добавлю еще один: SWIG
Он прост в изучении, делает много правильных вещей и поддерживает множество других языков, поэтому время, потраченное на изучение, может быть довольно полезным.
Если вы используете SWIG, вы создаете новый модуль расширения Python, но SWIG делает большую часть тяжелой работы за вас.
источник
Лично я написал бы модуль расширения на C. Не пугайтесь расширений Python C - их совсем нетрудно написать. Документация очень понятная и полезная. Когда я впервые написал расширение C на Python, я подумал, что мне понадобилось около часа, чтобы понять, как его написать - совсем немного времени.
источник
ctypes хорош, когда у вас уже есть скомпилированный большой двоичный объект библиотеки (например, библиотеки ОС). Затраты на вызовы являются серьезными, однако, если вы будете делать много вызовов в библиотеку, и вы все равно будете писать код C (или, по крайней мере, его компилировать), я бы сказал, чтобы цитон . Это не намного больше работы, и будет намного быстрее и более питонно использовать полученный pyd-файл.
Лично я склонен использовать cython для быстрого ускорения кода на python (циклы и целочисленные сравнения - это две области, в которых особенно ярко проявляется cython), и когда будет задействован какой-то более сложный код / перенос других библиотек, я перейду к Boost.Python . Boost.Python может быть сложен в настройке, но как только он заработает, он упрощает перенос кода C / C ++.
cython также хорош в упаковке numpy (что я узнал из материалов SciPy 2009 ), но я не использовал numpy, поэтому я не могу это комментировать.
источник
Если у вас уже есть библиотека с определенным API, я думаю, что
ctypes
это лучший вариант, так как вам нужно только немного инициализировать, а затем более или менее вызвать библиотеку так, как вы привыкли.Я думаю, что Cython или создание модуля расширения в C (что не очень сложно) более полезны, когда вам нужен новый код, например, вызов этой библиотеки и выполнение некоторых сложных, трудоемких задач, а затем передача результата в Python.
Другой подход для простых программ - напрямую выполнить другой процесс (скомпилированный извне), выводить результат в стандартный вывод и вызывать его с помощью модуля подпроцесса. Иногда это самый простой подход.
Например, если вы делаете консольную программу C, которая работает более или менее таким образом
Вы можете позвонить из Python
С небольшим форматированием строки вы можете получить результат любым способом. Вы также можете зафиксировать стандартный вывод ошибок, поэтому он достаточно гибкий.
источник
shell=True
может легко привести к некоторой уязвимости, когда пользователь действительно получает оболочку. Хорошо, когда разработчик является единственным пользователем, но в мире есть целая куча раздражающих уколов, просто ожидающих чего-то подобного.Есть одна проблема, которая заставила меня использовать ctypes, а не cython, и которая не упоминается в других ответах.
При использовании ctypes результат не зависит от используемого компилятора. Вы можете написать библиотеку, используя более или менее любой язык, который может быть скомпилирован в нативную общую библиотеку. Неважно, какая система, какой язык и какой компилятор. Cython, однако, ограничен инфраструктурой. Например, если вы хотите использовать компилятор Intel для Windows, гораздо сложнее заставить Cython работать: вы должны «объяснить» компилятор для Cython, перекомпилировать что-то с помощью этого точного компилятора и т. Д., Что существенно ограничивает переносимость.
источник
Если вы нацелены на Windows и решили обернуть некоторые проприетарные библиотеки C ++, то вскоре вы обнаружите, что разные версии
msvcrt***.dll
(Visual C ++ Runtime) слегка несовместимы.Это означает, что вы, возможно, не сможете использовать,
Cython
поскольку результатwrapper.pyd
связан сmsvcr90.dll
(Python 2.7) илиmsvcr100.dll
(Python 3.x) . Если библиотека, которую вы упаковываете, связана с другой версией среды выполнения, то вам не повезло.Затем, чтобы все заработало, вам нужно создать оболочки C для библиотек C ++, связать эту DLL-оболочку с той же версией,
msvcrt***.dll
что и ваша библиотека C ++. А затем используйтеctypes
для динамической загрузки dll-оболочки, упакованной вручную, во время выполнения.Итак, есть много мелких деталей, которые подробно описаны в следующей статье:
«Красивые родные библиотеки (на Python) »: http://lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/
источник
Есть также одна возможность использовать GObject Introspection для библиотек, которые используют GLib .
источник
Я знаю, что это старый вопрос, но этот вопрос возникает в Google, когда вы ищете что-то подобное
ctypes vs cython
, и большинство ответов здесь написаны теми, кто уже владеет знаниями,cython
илиc
которые могут не отражать фактического времени, которое вам нужно было потратить, чтобы изучить эти вопросы. реализовать ваше решение. Я полный новичок в обоих. Я никогда не трогалcython
раньше, и у меня очень мало опытаc/c++
.В течение последних двух дней я искал способ делегировать часть моего кода, требующую высокой производительности, на более низкий уровень, чем python. Я реализовал свой код в
ctypes
иCython
, который состоял в основном из двух простых функций.У меня был огромный список строк, которые нужно обработать. Обратите внимание
list
иstring
. Оба типа не полностью соответствуют типам inc
, потому что строки python по умолчанию являются юникодом, аc
строки - нет. Списки в python просто НЕ являются массивами c.Вот мой вердикт. Использование
cython
. Он более свободно интегрируется с python и с ним легче работать. Если что-то идет не так,ctypes
просто выдает вам segfault, по крайней мереcython
, когда это возможно, вы получите предупреждения о компиляции с трассировкой стека, и вы можете легко вернуть действительный объект python с помощьюcython
.Вот подробный отчет о том, сколько времени мне нужно было потратить на то и другое, чтобы реализовать одну и ту же функцию. Кстати, я очень мало занимался программированием на C / C ++:
Ctypes:
c
работает ли код.c
кода.c
код в реальную базу кода и увидел, чтоctypes
он плохо работает сmultiprocessing
модулем, так как его обработчик по умолчанию не выбирается.multiprocessing
модуль, и повторил попытку.c
коде генерировала ошибки в моей кодовой базе, хотя и прошла тестовый код. Ну, это, наверное, моя вина, что я плохо проверил крайние случаи, я искал быстрое решение.c
кода, и на второй или третьей итерации цикла python, который ее использует, у меня былаUnicodeError
мысль не декодировать байт в некоторой позиции, хотя я кодировал и декодировал все явно.На данный момент, я решил искать альтернативу и решил изучить
cython
:setuptools
вместоdistutils
.setup.py
для использования скомпилированного модуля в моей кодовой базе.multiprocessing
версии кодовой базы. Оно работает.Для справки, я, конечно, не измерил точные сроки моих инвестиций. Вполне возможно, что мое восприятие времени было немного внимательным из-за умственных усилий, которые требовались, когда я имел дело с ctypes. Но это должно передать чувство борьбы
cython
иctypes
источник