Предупреждение: мнение разработчика ядра Cython впереди.
Я почти всегда рекомендую Cython поверх ctypes. Причина в том, что у него гораздо более плавный путь обновления. Если вы используете ctypes, поначалу многие вещи будут простыми, и, безусловно, здорово написать свой код FFI на простом Python, без компиляции, сборок зависимостей и всего такого. Тем не менее, в какой-то момент вы почти наверняка обнаружите, что вам приходится часто заходить в свою библиотеку C, либо в цикле, либо в более длинных сериях взаимозависимых вызовов, и вы хотели бы ускорить это. В этот момент вы заметите, что вы не можете сделать это с помощью ctypes. Или, когда вам нужны функции обратного вызова и вы обнаружите, что ваш код обратного вызова Python становится узким местом, вы хотели бы ускорить его и / или перенести его также в C. Опять же, вы не можете сделать это с ctypes. Таким образом, вы должны переключить языки в этот момент и начать переписывать части своего кода, что может привести к обратному преобразованию кода Python / ctypes в обычный C, тем самым испортив все преимущества написания кода на простом Python.
С помощью Cython, OTOH, вы можете совершенно свободно делать код переноса и вызова таким тонким или толстым, как вам нужно. Вы можете начать с простых вызовов в ваш C-код из обычного Python-кода, и Cython переведет их в нативные C-вызовы без каких-либо дополнительных затрат на вызовы и с чрезвычайно низкими издержками преобразования для параметров Python. Когда вы заметите, что вам нужно еще больше производительности в какой-то момент, когда вы делаете слишком много дорогостоящих вызовов в вашу библиотеку C, вы можете начать аннотировать окружающий код Python статическими типами и позволить Cython оптимизировать его прямо на C для вас. Или вы можете начать переписывать части вашего C-кода на Cython, чтобы избежать вызовов, а также специализировать и алгоритмически затягивать ваши циклы. А если вам нужен быстрый обратный вызов, просто напишите функцию с соответствующей сигнатурой и передайте ее непосредственно в реестр обратных вызовов C. Опять же, никаких накладных расходов, и это дает вам простую производительность C-вызовов. И в гораздо менее вероятном случае, когда вы действительно не можете получить свой код достаточно быстро в Cython, вы все равно можете рассмотреть возможность переписать действительно важные его части в C (или C ++ или Fortran) и вызывать его из своего кода Cython естественным и естественным образом. Но тогда это действительно станет последним средством вместо единственного варианта.
Итак, ctypes хорош для простых вещей и быстрого запуска. Однако, как только ситуация начнет расти, вы, скорее всего, придете к тому, что заметите, что лучше использовать Cython с самого начала.