高级 F2PY 用例#

将用户定义的函数添加到 F2PY 生成的模块#

用户定义的 Python C/API 函数可以使用 usercodepymethoddef 语句在签名文件中定义(它们必须在 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 type> 到 <C type> 映射. 例如,如果 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 type>. <C type> 可以是以下之一:

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

为了将 int64real64 转换为有效的 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 注释中为相应的参数添加一个额外的声明来建立所需的连接,该声明在 character selector 部分中指定长度.例如,考虑一个 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'
>>>