Вопрос

Я некоторое время подумывал о написании библиотеки пиковой подгонки.Я достаточно хорошо знаю Python и планирую для начала реализовать все на Python, но предполагаю, что в конечном итоге мне, возможно, придется повторно реализовать некоторые основные процедуры на скомпилированном языке.

IIRC, одна из первоначальных задач Python заключалась в создании языка прототипирования, однако Python довольно либерален в разрешении передачи функций, функторов и объектов в функции и методы, тогда как я подозреваю, что то же самое нельзя сказать, скажем, о C или Fortran.

Что мне следует знать о разработке функций/классов, которые, как я предполагаю, должны будут взаимодействовать с скомпилированным языком?И какая часть этих потенциальных проблем решается такими библиотеками, как cTypes, bgen, СВИГ, Boost.Python, Китон или Python SIP?

В этом конкретном случае использования (библиотека подгонки) я предполагаю, что пользователи смогут определять математические функции (Гуассиан, Лоренц и т. д.) как функции Python, которые затем могут быть переданы для интерпретации библиотекой подгонки скомпилированного кода.Передача и возврат массивов также важны.

Это было полезно?

Решение

Наконец-то вопрос, на который я действительно могу дать ценный ответ :).

Для своей работы (доктор философии по методам оптических измерений) я исследовал f2py, boost.python, swig, cython и пирекс.Я широко использовал swig, немного boost.python, а также Pyrex и cython.Я также использовал ctypes.Это моя поломка:

Отказ от ответственности:Это мой личный опыт.Я не участвую ни в одном из этих проектов.

глоток:не очень хорошо работает с C++.Так и должно быть, но проблемы с искажением имен на этапе связывания были для меня большой головной болью в Linux и Mac OS X.Если у вас есть код C и вы хотите, чтобы он был связан с Python, это хорошее решение.Я обернул GTS для своих нужд, и мне нужно было написать общую библиотеку C, к которой я мог бы подключиться.Я бы не рекомендовал это.

Типы:Я написал оболочку libdc1394 (библиотека IEEE Camera), используя ctypes, и это был очень простой опыт.Вы можете найти код на https://launchpad.net/pydc1394.Преобразование заголовков в код Python — это большой труд, но тогда все работает надежно.Это хороший способ, если вы хотите подключить внешнюю библиотеку.Ctypes также находится в стандартной библиотеке Python, поэтому каждый может сразу использовать ваш код.Это также хороший способ быстро поиграться с новой библиотекой Python.Я могу порекомендовать его для взаимодействия с внешними библиотеками.

Boost.Python:Очень приятный.Если у вас уже есть собственный код C++, который вы хотите использовать в Python, сделайте это.Таким образом очень легко преобразовать структуры классов C++ в структуры классов Python.Я рекомендую это, если у вас есть код C++, который вам нужен на Python.

Пирекс/Цитон: Используйте Cython, а не Pyrex.Период.Cython более продвинут и приятен в использовании.Сейчас я делаю с Cython все, что раньше делал с SWIG или Ctypes.Это также лучший способ, если у вас есть код Python, который работает слишком медленно.Процесс совершенно фантастический:вы конвертируете свои модули Python в модули Cython, создаете их и продолжаете профилировать и оптимизировать, как будто это все еще был Python (никаких изменений в инструментах не требуется).Затем вы можете применить столько же (или меньше) кода C, смешанного с вашим кодом Python.Это намного быстрее, чем переписывать целые части вашего приложения на C;вы только переписываете внутренний цикл.

Тайминги:ctypes имеет самые высокие накладные расходы на вызовы (~700 нс), за ним следует boost.python (322 нс), а затем непосредственно swig (290 нс).У Cython самые низкие накладные расходы на вызовы (124 нс) и лучшая обратная связь, на которую он тратит время (поддержка cProfile!).Числа взяты из моего ящика, вызывающего тривиальную функцию, которая возвращает целое число из интерактивной оболочки;Таким образом, накладные расходы на импорт модуля не синхронизируются, а учитываются только накладные расходы на вызов функций.Поэтому проще и продуктивнее всего быстро получить код Python путем профилирования и использования cython.

Краткое содержание:Для вашей проблемы используйте Cython;).Я надеюсь, что это краткое изложение будет полезно для некоторых людей.Я с радостью отвечу на любой оставшийся вопрос.


Редактировать:Забываю упомянуть:для числовых целей (то есть подключения к NumPy) используйте Cython;у них есть для этого поддержка (потому что они в основном разрабатывают Cython для этой цели).Так что это должно быть еще одним +1 к вашему решению.

Другие советы

Я не использовал SWIG или SIP, но считаю, что писать оболочки Python с помощью boost.python быть очень мощным и относительно простым в использовании.

Мне неясно, каковы ваши требования к передаче типов между C/C++ и Python, но вы можете легко сделать это, либо представив тип C++ для Python, либо используя универсальный boost::python::object аргумент вашего C++ API.Вы также можете зарегистрировать преобразователи для автоматического преобразования типов Python в типы C++ и наоборот.

Если вы планируете использовать boost.python, руководство это хорошее место для начала.

Я реализовал что-то похожее на то, что вам нужно.У меня есть функция C ++, которая принимает функцию Python и изображение в качестве аргументов, и применяет функцию Python к каждому пикселю в изображении.

Image* unary(boost::python::object op, Image& im)
{
    Image* out = new Image(im.width(), im.height(), im.channels());
    for(unsigned int i=0; i<im.size(); i++)
    {
        (*out)[i] == extract<float>(op(im[i]));
    }
    return out;
}

В данном случае Image — это объект C++, доступный для Python (изображение с плавающими пикселями), а op — это функция, определенная Python (или любой объект Python с атрибутом __call__).Затем вы можете использовать эту функцию следующим образом (при условии, что унарная функция находится в вызываемом изображении, которое также содержит изображение и функцию загрузки):

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

Что касается использования массивов с повышением, лично я этого не делал, но знаю, что доступна функция предоставления массивов для Python с использованием повышения — этот может быть полезно.

Лучший способ спланировать возможный переход к скомпилированному коду — написать части, чувствительные к производительности, в виде модуля простых функций в функциональный стиль (без сохранения состояния и без побочных эффектов), которые принимают и возвращают базовые типы данных.

Это обеспечит однозначное сопоставление кода прототипа Python с конечным скомпилированным кодом и позволит вам использовать cтипы легко и избежать целой кучи головных болей.

Для пиковой подгонки вам почти наверняка придется использовать массивы, что немного усложнит ситуацию, но все же вполне осуществимо с помощью ctypes.

Если вы действительно хотите использовать более сложные структуры данных или изменить переданные аргументы, СВИГ или Стандартный интерфейс расширения C Python позволит вам делать то, что вы хотите, но с некоторыми хлопотами.

Что вы делаете, вы также можете проверить NumPy, который может выполнить часть работы, которую вы хотели бы перенести на C, а также предложить некоторая дополнительная помощь в перемещении данных туда и обратно между Python и C.

f2py (часть numpy) — это более простая альтернатива SWIG и boost.python для упаковки кода обработки чисел на C/Fortran.

По моему опыту, есть два простых способа вызвать код C из кода Python.Существуют и другие подходы, каждый из которых более раздражает и/или многословен.

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

Второй самый простой способ — написать модуль Python на C, а затем вызывать функции в этом модуле.Вы можете передать в эти функции C все, что захотите, без необходимости проходить через какие-либо препятствия.И из этих функций C легко вызывать функции или методы Python, как описано здесь: https://docs.python.org/extending/extending.html#calling-python-functions-from-c

У меня недостаточно опыта работы со SWIG, чтобы давать разумные комментарии.И хотя можно делать такие вещи, как передача пользовательских объектов Python в функции C через ctypes или определение новых классов Python в C, эти вещи раздражают и многословны, и я рекомендую использовать один из двух подходов, описанных выше.

Python довольно либерален в разрешении передачи функций, функторов и объектов в функции и методы, тогда как я подозреваю, что то же самое нельзя сказать, скажем, о C или Fortran.

В C вы не можете передать функцию в качестве аргумента функции, но вы можете передать указатель на функцию, что так же хорошо, как функция.

Я не знаю, насколько это поможет, когда вы пытаетесь интегрировать код C и Python, но я просто хотел прояснить одно заблуждение.

В дополнение к вышеперечисленным инструментам я могу порекомендовать использовать Пирекс (для создания модулей расширения Python) или Псико (как JIT-компилятор для Python).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top