Вызов функции cdef
соответствует более или менее просто переходу к адресу в памяти - той, с которой команда должна быть прочитана / выполнена.Вопрос в том, как этот адрес предоставляется.Есть несколько случаев, которые нам нужно рассмотреть:
A. встроенные функции
Код этих функций либо встроенный, либо определение функции в том же переводеединицы, таким образом, адрес известен компоновщику во время соединения - нет необходимости в дополнительных библиотеках.
Примером являются библиотеки только для заголовков.
Последствия: Ничегоэто должно быть сделано в setup.py
.
B. статическое связывание
Определение / функциональность, которые нам нужны, находятся в другом модуле / библиотеке перевода - целевой-адресПереход вычисляется во время соединения и больше не может быть изменен впоследствии.
Примером являются дополнительные c / cpp-файлы или статические библиотеки, которые добавляются в определение расширения.
Последствия: Статическая библиотека должна быть добавлена к setup.py
, то есть путь к библиотеке и имя библиотеки.
C. динамическое связывание
Предоставлена предоставленная функциональностьв общем объекте / dll.Адрес для перехода вычисляется во время выполнения из загрузчика и может быть заменен при запуске программы путем обмена загруженными общими объектами.
В качестве примера можно привести stdlibc ++ (обычно добавляемый автоматически g ++) или libm, который не является автоматическидобавлено в команду компоновщика с помощью gcc.
Последствия: Динамическая библиотека должна быть добавлена к setup.py
, то есть путь к библиотеке и имя библиотеки, возможно, r-путь.поэтому / DLL должен быть предоставлен во время выполнения.В этом SO-посте можно найти гораздо больше информации о Cython / Python с использованием динамических библиотек.
D. Вызов через указатель
Линкер необходим только тогда, когда мы вызываемфункция через свое имя.Если мы вызываем его через указатель на функцию, нам не нужен компоновщик / загрузчик, потому что адрес функции уже известен - значение в указателе функции.
Пример: генерируемые Cython модули используют этомеханизм, обеспечивающий доступ к его функциям cdef, экспортированным через pxd
-файл.Он создает структуру данных (которая хранится в виде переменной __pyx_capi__
в самом модуле) указателей на функции, которая заполняется загрузчиком после загрузки so / dll.
Мы можем ожидать, что дляпример
#foo.pyx:
cdef void doit():
print("doit")
#foo.pxd
cdef void doit()
>>> cythonize -3 -i foo.pyx
>>> python -c "import foo; print(foo.__pyx_capi__)"
{'doit': <capsule object "void (void)" at 0x7f7b10bb16c0>}
Теперь, вызов функции cdef
из другого модуля просто переходит на соответствующий адрес.
Последствия: Нам нужно импортировать необходимую функциональность.
Тем не менее, теперь, после того, как все это написали, я понимаю, что numpy - это не один из вышеперечисленных случаев (я всегда предполагал, что это смесь A и D, но больше не уверен)- скоро обновлю ответ относительно numpy.