Расширенные примеры использования F2PY
Добавление пользовательских функций в модули, сгенерированные F2PY
Пользовательские Python-функции C/API могут быть определены внутри файлов сигнатур с помощью инструкций usercode и pymethoddef (они должны использоваться внутри блока python module). Например, следующий файл сигнатур spam.pyf
! -*- f90 -*-
python module spam
usercode '''
static char doc_spam_system[] = "Execute a shell command.";
static PyObject *spam_system(PyObject *self, PyObject *args)
{
char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return Py_BuildValue("i", sts);
}
'''
pymethoddef '''
{"system", spam_system, METH_VARARGS, doc_spam_system},
'''
end python module spam
оборачивает функцию C-библиотеки system():
f2py -c spam.pyf
В Python это можно использовать следующим образом:
>>> import spam
>>> status = spam.system('whoami')
pearu
>>> status = spam.system('blah')
sh: line 1: blah: command not found
Добавление пользовательских переменных
Следующий пример демонстрирует, как добавить пользовательские переменные в модуль расширения, сгенерированный F2PY, изменив словарь сгенерированного модуля F2PY. Рассмотрим следующий файл сигнатур (скомпилированный с помощью f2py -c var.pyf):
! -*- f90 -*-
python module var
usercode '''
int BAR = 5;
'''
interface
usercode '''
PyDict_SetItemString(d,"BAR",PyInt_FromLong(BAR));
'''
end interface
end python module
Обратите внимание, что вторая usercode инструкция должна быть определена внутри блока interface, а словарь модуля доступен через переменную d (см. varmodule.c сгенерированное f2py var.pyf для получения дополнительных сведений).
Использование в Python:
>>> import var >>> var.BAR 5
Обработка спецификаторов KIND
В настоящее время F2PY может обрабатывать только объявления <type spec>(kind=<kindselector>), где <kindselector> — это числовое целое число (например, 1, 2, 4,…), но не вызов функции KIND(..) или любое другое выражение. F2PY должен знать, какой соответствующий тип C, а общее решение для этого было бы слишком сложно для реализации.
Однако F2PY предоставляет крючок для преодоления этой трудности, а именно, пользователи могут определить свои собственные отображения <тип Fortran> на <тип C>. Например, если код Fortran 90 содержит:
REAL(kind=KIND(0.0D0)) ...
тогда создайте файл отображения, содержащий словарь Python:
{'real': {'KIND(0.0D0)': 'double'}}
например.
Используйте опцию командной строки --f2cmap для передачи имени файла в F2PY. По умолчанию F2PY предполагает, что имя файла .f2py_f2cmap в текущей рабочей директории.
Более общим образом, файл f2cmap должен содержать словарь со следующими элементами:
<Fortran typespec> : {<selector_expr>:<C type>}
который определяет отображение между типом Fortran:
<Fortran typespec>([kind=]<selector_expr>)
и соответствующим <типом C>. <Тип C> может быть одним из следующих:
double float long_double char signed_char unsigned_char short unsigned_short int long long_long unsigned complex_float complex_double complex_long_double string
Например, для файла Fortran func1.f содержащего:
subroutine func1(n, x, res)
use, intrinsic :: iso_fortran_env, only: int64, real64
implicit none
integer(int64), intent(in) :: n
real(real64), intent(in) :: x(n)
real(real64), intent(out) :: res
Cf2py intent(hide) :: n
res = sum(x)
end
Для преобразования int64 и real64 в допустимые типы данных C, можно создать файл .f2py_f2cmap в текущей директории со следующим содержанием:
dict(real=dict(real64='double'), integer=dict(int64='long long'))
и создать модуль как обычно. F2PY проверяет, присутствует ли файл .f2py_f2cmap в текущей директории, и будет использовать его для сопоставления спецификаторов KIND с типами данных C.
f2py -c func1.f -m func1
В качестве альтернативы, файл отображения можно сохранить под любым другим именем, например, mapfile.txt, и эту информацию можно передать в F2PY, используя опцию --f2cmap.
f2py -c func1.f -m func1 --f2cmap mapfile.txt
Для получения дополнительной информации см. исходный код F2Py numpy/f2py/capi_maps.py.
Строки символов
Строки символов с предположительной длиной
В Fortran аргументы строк символов с предположительной длиной объявляются как character*(*) или character(len=*), то есть длина таких аргументов определяется фактическими строковыми аргументами во время выполнения. Для аргументов intent(in) это отсутствие информации о длине не создает проблем для f2py при построении функциональных оберток. Однако для аргументов intent(out) отсутствие информации о длине представляет проблему для сгенерированных f2py оберток, поскольку для создания буферов памяти для таких аргументов нет информации о размере, и F2PY предполагает, что длина равна 0. В зависимости от того, как задается длина строк с предположительной длиной, существуют способы решения этой проблемы, как показано ниже.
Если длина аргумента вывода character*(*) определяется состоянием других входных аргументов, необходимое соединение может быть установлено в файле сигнатуры или внутри комментария f2py путем добавления дополнительного объявления для соответствующего аргумента, который указывает длину в части селектора символов. Например, рассмотрим файл Fortran asterisk1.f90:
subroutine foo1(s) character*(*), intent(out) :: s !f2py character(f2py_len=12) s s = "123456789A12" end subroutine foo1
Скомпилируйте его с помощью f2py -c asterisk1.f90 -m asterisk1 и затем в Python:
>>> import asterisk1 >>> asterisk1.foo1() b'123456789A12'
Обратите внимание, что дополнительное объявление character(f2py_len=12) s интерпретируется только f2py, а в спецификации f2py_len= можно использовать C-выражения в качестве значения длины.
В следующем примере:
subroutine foo2(s, n) character(len=*), intent(out) :: s integer, intent(in) :: n !f2py character(f2py_len=n), depend(n) :: s s = "123456789A123456789B"(1:n) end subroutine foo2
длина выходной строки с предположительной длиной зависит от входного аргумента n, после обертывания с помощью F2PY, в Python:
>>> import asterisk >>> asterisk.foo2(2) b'12' >>> asterisk.foo2(12) b'123456789A12' >>>
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/f2py/advanced/use_cases.html