高级 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
获取(有关更多详细信息,请参阅 f2py var.pyf
生成的 varmodule.c
)。
在 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
,并且可以使用 --f2cmap
选项将此信息传递给 F2PY。
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'
>>>