Расширенные случаи использования 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",PyLong_FromLong(BAR));
'''
end interface
end python module
Обратите внимание, что второй usercode оператор должен быть определён внутри interface блок и словарь модуля доступен через переменную d (см. varmodule.c сгенерировано f2py var.pyf для
дополнительных деталей).
Использование в Python:
>>> import var
>>> var.BAR
5
Работа с спецификаторами KIND#
В настоящее время F2PY может обрабатывать только
объявления, где является числовым целым числом (например, 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>)
и соответствующий
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'
>>>