签名文件#

接口定义文件 (.pyf) 是你可以在 Python 和 Fortran 之间进行精细调整的方式.签名文件 (.pyf 文件) 的语法规范是基于 Fortran 90/95 语言规范设计的.几乎所有的 Fortran 标准构造在自由格式和固定格式中都能被理解(记得 Fortran 77 是 Fortran 90/95 的一个子集).F2PY 引入了一些 Fortran 90/95 语言规范的扩展,这些扩展有助于设计 Fortran 到 Python 的接口,使其更加”Pythonic”.

签名文件可能包含任意的 Fortran 代码,因此任何 Fortran 90/95 代码都可以被视为签名文件.F2PY 会静默忽略与创建接口无关的 Fortran 构造.然而,这也意味着语法错误不会被 F2PY 捕获,只有在构建库时才会被捕获.

备注

目前,F2PY 可能无法处理某些有效的 Fortran 结构.如果发生这种情况,您可以查看 NumPy GitHub 问题跟踪器 以获取可能的解决方法或正在进行中的想法.

一般来说,签名文件的内容是区分大小写的.当扫描 Fortran 代码以生成签名文件时,F2PY 会自动将所有情况降低,除非在多行块中或使用 --no-lower 选项时.

签名文件的语法如下所示.

签名文件语法#

Python 模块块#

一个签名文件可以包含一个(推荐)或多个 python 模块 块.``python 模块`` 块描述了 F2PY 生成的 Python/C 扩展模块 <modulename>module.c 的内容.

警告

异常:如果 <modulename> 包含一个子字符串 __user__,那么相应的 python 模块 块描述了回调函数的签名(见 回调参数).

一个 python 模块 块具有以下结构:

python module <modulename>
  [<usercode statement>]...
  [
  interface
    <usercode statement>
    <Fortran block data signatures>
    <Fortran/C routine signatures>
  end [interface]
  ]...
  [
  interface
    module <F90 modulename>
      [<F90 module data type declarations>]
      [<F90 module routine signatures>]
    end [module [<F90 modulename>]]
  end [interface]
  ]...
end [python module [<modulename>]]

这里,方括号 [] 表示一个可选部分,点 ... 表示一个或多个前一部分.因此,``[]…`` 应理解为零个或多个前一部分.

Fortran/C 例程签名#

Fortran 例程的签名具有以下结构:

[<typespec>] function | subroutine <routine name> \
              [ ( [<arguments>] ) ] [ result ( <entityname> ) ]
  [<argument/variable type declarations>]
  [<argument/variable attribute statements>]
  [<use statements>]
  [<common block statements>]
  [<other statements>]
end [ function | subroutine [<routine name>] ]

从 Fortran 例程签名 F2PY 生成一个具有以下签名的 Python/C 扩展函数:

def <routine name>(<required arguments>[,<optional arguments>]):
     ...
     return <return variables>

Fortran 块数据的签名具有以下结构:

block data [ <block data name> ]
  [<variable type declarations>]
  [<variable attribute statements>]
  [<use statements>]
  [<common block statements>]
  [<include statements>]
end [ block data [<block data name>] ]

类型声明#

<参数/变量类型声明> 部分的定义是

<typespec> [ [<attrspec>] :: ] <entitydecl>

哪里

<typespec> := byte | character [<charselector>]
           | complex [<kindselector>] | real [<kindselector>]
           | double complex | double precision
           | integer [<kindselector>] | logical [<kindselector>]

<charselector> := * <charlen>
               | ( [len=] <len> [ , [kind=] <kind>] )
               | ( kind= <kind> [ , len= <len> ] )
<kindselector> := * <intlen> | ( [kind=] <kind> )

<entitydecl> := <name> [ [ * <charlen> ] [ ( <arrayspec> ) ]
                      | [ ( <arrayspec> ) ] * <charlen> ]
                     | [ / <init_expr> / | = <init_expr> ] \
                       [ , <entitydecl> ]

  • <attrspec> 是一个属性_的逗号分隔列表;

  • <arrayspec> 是一个由维度边界组成的逗号分隔列表;

  • <init_expr> 是一个 C 表达式;

  • <intlen> 对于 integer 类型规范可以是负整数.在这种情况下,``integer*<negintlen>`` 表示无符号的 C 整数;

如果一个参数没有 <参数类型声明> ,它的类型是通过对其名称应用 隐式 规则来确定的.

Statements#

属性声明#

<参数/变量属性声明> 类似于 <参数/变量类型声明>,但没有 <类型说明>.

属性声明不能包含其他属性,并且 <entitydecl> 只能是一个名称列表.有关 F2PY 可以使用哪些属性的更多详细信息,请参见 属性.

使用声明#

  • <use statement> 部分的定义是

    use <modulename> [ , <rename_list> | , ONLY : <only_list> ]
    

    哪里

    <rename_list> := <local_name> => <use_name> [ , <rename_list> ]
    
  • 目前 F2PY 仅在链接回调模块和 external 参数(回调函数)时使用 use 语句.请参见 回调参数.

常见的块语句#

  • <common block statement> 部分的定义是

    common / <common name> / <shortentitydecl>
    

    哪里

    <shortentitydecl> := <name> [ ( <arrayspec> ) ] [ , <shortentitydecl> ]
    
  • 如果一个 python 模块 块包含两个或更多同名的 common 块,则来自额外声明的变量会被追加.``<shortentitydecl>`` 中的变量类型使用 <argument type declarations> 定义.请注意,相应的 <argument type declarations> 可能包含数组规范;那么这些不需要在 <shortentitydecl> 中指定.

其他声明#

  • <其他语句> 部分指的是任何未在上文描述的 Fortran 语言构造.F2PY 忽略其中的大部分,除了以下内容:

    • call 语句和 external 参数的函数调用(参见 更多关于外部参数的详细信息);

    • include 语句
      include '<filename>'
      include "<filename>"
      

      如果文件 <filename> 不存在,则 include 语句将被忽略.否则,文件 <filename> 将被包含到签名文件中.``include`` 语句可以在签名文件的任何部分使用,也可以在 Fortran/C 例程签名块之外使用.

    • implicit 声明
      implicit none
      implicit <list of implicit maps>
      

      哪里

      <implicit map> := <typespec> ( <list of letters or range of letters> )
      

      如果没有使用 <变量类型声明> 定义变量,隐式规则用于确定变量的类型规范(从其名称的第一个字母开始).默认的隐式规则如下:

      implicit real (a-h,o-z,$_), integer (i-m)
      
    • entry 语句
      entry <entry name> [([<arguments>])]
      

      F2PY 使用例程块的签名生成所有入口名称的包装器.

      备注

      entry 语句可以用来描述任意子程序或函数的签名,允许 F2PY 从一个例程块签名生成多个包装器.这样做时有一些限制:不能使用 fortranname,只有当它们对所有入口例程都有效时,才能使用 callstatementcallprotoargument 等.

F2PY 语句#

此外,F2PY 引入了以下语句:

threadsafe

在调用 Fortran/C 函数时,使用 Py_BEGIN_ALLOW_THREADS .. Py_END_ALLOW_THREADS 块.

callstatement <C-expr|多行块>

将 F2PY 生成的调用 Fortran/C 函数的语句替换为 <C-expr|多行块>.包装的 Fortran/C 函数可用作 (*f2py_func).

要引发异常,请在 <C-expr|multi-line block> 中设置 f2py_success = 0.

callprotoargument <C-typespecs>

当使用 callstatement 语句时,F2PY 可能不会为 Fortran/C 函数生成正确的原型(因为 <C-expr> 可能包含函数调用,而 F2PY 无法确定应该是什么样的正确原型).

通过此声明,您可以显式指定相应原型的参数:

extern <return type> FUNC_F(<routine name>,<ROUTINE NAME>)(<callprotoargument>);
fortranname [<实际的 Fortran/C 例程名>]

F2PY 允许为给定的 Fortran/C 函数使用任意的 <routine name>.然后此语句用于 <实际的 Fortran/C 函数名>.

如果使用了 fortranname 语句而没有 <实际的 Fortran/C 例程名称> ,则会生成一个虚拟包装器.

usercode <多行块>

当在 python 模块 块内使用时,给定的 C 代码将被插入到生成的 C/API 源代码中,就在包装函数定义之前.

在这里,您可以定义任意C函数,用于可选参数的初始化.

例如,如果在 python 模块 块中两次使用 usercode,则第二个多行块在定义外部例程之后插入.

当在 <routine signature> 内部使用时,给定的 C 代码将被插入到相应的包装函数中,正好在变量声明之后但在任何 C 语句之前.因此,``usercode`` 后续内容可以包含声明和 C 语句.

当在第一个 interface 块内使用时,给定的 C 代码将被插入到扩展模块的初始化函数的末尾.这就是如何修改扩展模块的字典,并且有许多用例;例如,定义额外的变量.

pymethoddef <多行块>

这是一个多行块,将被插入到模块方法定义的 PyMethodDef 数组中.它必须是一个逗号分隔的 C 数组列表(详见 扩展和嵌入 Python 文档).``pymethoddef`` 语句只能在 python 模块 块内使用.

属性#

以下属性可以被 F2PY 使用.

optional

相应的参数被移动到 <可选参数> 列表的末尾.可以通过 <init_expr> 为可选参数指定默认值(参见 entitydecl 定义

备注

  • 默认值必须给定为有效的C表达式.

  • 每当使用 <init_expr> 时,F2PY 会自动设置 optional 属性.

  • 对于一个可选的数组参数,其所有维度必须是有界的.

required

与此属性对应的参数被认为是必需的.这是默认设置.只有在使用 <init_expr> 时需要禁用自动 optional 设置的情况下,才应指定 required.

如果一个 Python None 对象被用作必需参数,该参数将被视为可选.也就是说,在数组参数的情况下,内存会被分配.如果给出了 <init_expr>,则进行相应的初始化.

dimension(<arrayspec>)

相应的变量被视为一个数组,其维度在 <arrayspec> 中给出.

intent(<intentspec>)

这指定了相应参数的”意图”.``<intentspec>`` 是一个逗号分隔的以下键的列表:

  • in

    相应的参数被认为是只输入的.这意味着参数的值被传递给一个 Fortran/C 函数,并且该函数被期望不会改变这个参数的值.

  • inout

    相应的参数被标记为输入/输出或作为 in situ 输出参数.``intent(inout)`` 参数只能是 连续 的 NumPy 数组(无论是 Fortran 还是 C 意义上的),具有适当类型和大小.后者与 NumPy 中使用的默认连续概念一致,只有在使用 intent(c) 时才有效.F2PY 默认假设 Fortran 连续参数.

    备注

    使用 intent(inout) 通常不推荐,因为它可能导致意外结果.例如,使用 intent(inout) 的标量参数被假定为数组对象,以便使 in situ 更改生效.请改用 intent(in,out).

    另请参见 intent(inplace) 属性.

  • inplace

    相应的参数被认为是输入/输出或 in situ 输出参数.``intent(inplace)`` 参数必须是适当大小的 NumPy 数组.如果数组的类型不”适当”或数组不连续,则数组将在原地修改以修复类型并使其连续.

    备注

    使用 intent(inplace) 通常也不推荐.

    例如,当从 intent(inplace) 参数中获取切片后,在进行原地更改后,切片的指针可能会指向未分配的内存区域.

  • out

    相应的参数被视为返回变量.它被附加到 <returned variables> 列表中.使用 intent(out) 会自动设置 intent(hide) ,除非同时指定了 intent(in)intent(inout).

    默认情况下,返回的多维数组是Fortran连续的.如果使用 intent(c) 属性,则返回的多维数组是C连续的.

  • hide

    相应的参数从必需或可选参数列表中移除.通常 intent(hide)intent(out) 一起使用,或者当 <init_expr> 完全确定参数的值时,如下例所示:

    integer intent(hide),depend(a) :: n = len(a)
    real intent(in),dimension(n) :: a
    
  • c

    相应的参数被视为C标量或C数组参数.对于标量参数的情况,其值作为C标量参数传递给C函数(请记住,Fortran标量参数实际上是C指针参数).对于数组参数,假定包装函数将多维数组视为C连续数组.

    对于一维数组,不需要使用 intent(c) ,无论被包装的函数是用 Fortran 还是 C 编写的.这是因为 Fortran 和 C 的连续性概念在一维情况下是重叠的.

    如果 intent(c) 被用作一个语句但没有实体声明列表,那么 F2PY 会将 intent(c) 属性添加到所有参数中.

    此外,在包装C函数时,必须为 <routine name> 使用 intent(c) 属性,以禁用特定于Fortran的 F_FUNC(..,..) 宏.

  • cache

    相应的参数被视为无用内存.不会进行Fortran或C的连续性检查.使用 intent(cache) 仅对数组参数有意义,也可以与 intent(hide)optional 属性结合使用.

  • copy

    确保 intent(in) 参数的原始内容被保留.通常与 intent(in,out) 属性一起使用.F2PY 创建一个可选参数 overwrite_<参数名> ,默认值为 0 .

  • overwrite

    这表明 intent(in) 参数的原始内容可能会被 Fortran/C 函数更改.F2PY 创建一个默认值为 1 的可选参数 overwrite_<参数名>.

  • out=<新名称>

    在包装函数的 __doc__ 字符串中将返回的名称替换为 <new name>.

  • callback

    构建一个适合从Fortran调用Python函数的外部函数.在相应的 external 语句之前必须指定 intent(callback) .如果’参数’不在参数列表中,则它将被添加到Python包装器中,但仅通过初始化外部函数来实现.

    备注

    在Fortran/C代码假设用户实现了一个具有给定原型的函数并将其链接到可执行文件的情况下,使用 intent(callback).如果函数出现在Fortran例程的参数列表中,请不要使用 intent(callback).

    使用 intent(hide)optional 属性指定,并且在参数列表中不指定回调函数的情况下使用包装函数;那么回调函数被假定在 F2PY 生成的扩展模块的命名空间中找到,用户可以将其设置为模块属性.

  • aux

    在 F2PY 生成的包装函数中定义一个辅助 C 变量.有助于保存参数值,以便它们可以在其他变量的初始化表达式中访问.

    备注

    intent(aux) 静默地暗示 intent(c).

以下规则适用:

  • 如果没有指定 intent(in | inout | out | hide) 中的任何一个,则假定为 intent(in).

    • intent(in,inout)intent(in);

    • intent(in,hide)intent(inout,hide)intent(hide);

    • intent(out) 除非指定了 intent(in)intent(inout),否则就是 intent(out,hide).

  • 如果使用 intent(copy)intent(overwrite) ,则会引入一个额外的可选参数,名称为 overwrite_<参数名> ,默认值分别为 0 或 1.

    • intent(inout,inplace)intent(inplace);

    • intent(in,inplace)intent(inplace);

    • intent(hide) 禁用 optionalrequired.

check([<C-booleanexpr>])

通过评估 <C-booleanexpr> 对参数执行一致性检查;如果 <C-booleanexpr> 返回 0,则会引发异常.

备注

如果没有使用 check(..) ,那么 F2PY 会自动生成一些标准检查(例如,对于数组参数,它会检查适当的形状和大小).使用 check() 来禁用 F2PY 生成的检查.

depend([<names>])

这声明相应的参数依赖于 <names> 列表中的变量值.例如, <init_expr> 可能使用其他参数的值.通过 depend(..) 属性提供的信息,F2PY 确保参数按适当的顺序初始化.如果未使用 depend(..) 属性,则 F2PY 会自动确定依赖关系.使用 depend() 禁用 F2PY 生成的依赖关系.

当你编辑最初由 F2PY 生成的依赖关系时,小心不要破坏其他相关变量的依赖关系.另一件需要注意的事情是循环依赖.F2PY 在构建包装器时能够检测循环依赖,如果发现任何循环依赖,它会报错.

allocatable

相应的变量是一个定义为 Fortran 90 模块数据的 Fortran 90 可分配数组.

external

相应的参数是由用户提供的函数.这个回调函数的签名可以被定义.

  • __user__ 模块块中,

  • 或者通过指示性(或真实的,如果签名文件是真实的 Fortran 代码)调用在 <其他语句> 块中.

例如,F2PY 从以下内容生成:

external cb_sub, cb_fun
integer n
real a(n),r
call cb_sub(a,n)
r = cb_fun(4)

以下回调签名:

subroutine cb_sub(a,n)
    real dimension(n) :: a
    integer optional,check(len(a)>=n),depend(a) :: n=len(a)
end subroutine cb_sub
function cb_fun(e_4_e) result (r)
    integer :: e_4_e
    real :: r
end function cb_fun

相应的用户提供的 Python 函数是:

def cb_sub(a,[n]):
    ...
    return
def cb_fun(e_4_e):
    ...
    return r

另请参见 intent(callback) 属性.

parameter

这表明相应的变量是一个参数,并且它必须有一个固定值.F2PY 将所有参数的出现替换为其相应的值.

扩展#

F2PY 指令#

F2PY 指令允许在 Fortran 77/90 源代码中使用 F2PY 签名文件构造.通过此功能,可以(几乎)完全跳过中间签名文件的生成,并直接将 F2PY 应用于 Fortran 源代码.

F2PY 指令具有以下形式:

<comment char>f2py ...

固定格式和自由格式 Fortran 代码允许的注释字符分别是 cC*!#!.紧跟在 <comment char>f2py 之后的所有内容都会被编译器忽略,但会被 F2PY 作为正常的非注释 Fortran 行读取:

备注

当 F2PY 发现一行带有 F2PY 指令时,该指令首先被 5 个空格替换,然后该行被重新读取.

对于固定格式的 Fortran 代码,``<comment char>`` 当然必须位于文件的第一列.对于自由格式的 Fortran 代码,F2PY 指令可以出现在文件的任何位置.

C 表达式#

C 表达式用于签名文件的以下部分:

  • <init_expr> 用于变量初始化;

  • <C-booleanexpr>check 属性;

  • <arrayspec>dimension 属性;

  • callstatement 语句,这里也可以使用C多行块.

一个C表达式可能包含:

  • 标准 C 结构;

  • 来自 math.hPython.h 的函数;

  • 参数列表中的变量,根据给定的依赖关系在之前初始化;

  • 以下 CPP 宏:

    f2py_rank(<name>)

    返回数组 <name> 的秩.

    f2py_shape(<name>, <n>)

    返回数组 <name> 的第 <n> 维.

    f2py_len(<name>)

    返回数组 <name> 的长度.

    f2py_size(<name>)

    返回数组 <name> 的大小.

    f2py_itemsize(<name>)

    返回数组 <name> 的项大小.

    f2py_slen(<name>)

    返回字符串 <name> 的长度.

对于初始化一个数组 <array name>,F2PY 生成一个遍历所有索引和维度的循环,执行以下伪语句:

<array name>(_i[0],_i[1],...) = <init_expr>;

其中 _i[<i>] 指的是第 <i> 个索引值,其范围从 0shape(<array name>,<i>)-1.

例如,一个函数 myrange(n) 由以下签名生成

subroutine myrange(a,n)
  fortranname        ! myrange is a dummy wrapper
  integer intent(in) :: n
  real*8 intent(c,out),dimension(n),depend(n) :: a = _i[0]
end subroutine myrange

等同于 numpy.arange(n,dtype=float).

警告

F2PY 在扫描 Fortran 代码时,也可能在 C 表达式中将字母转换为小写(参见 --[no]-lower 选项).

多行块#

一个多行块以 ''' (三个单引号)开始,并在某些 严格 后续行中以 ''' 结束.多行块只能在 .pyf 文件中使用.多行块的内容可以是任意的(除了不能包含 '''),并且不对内容进行任何转换(例如降低大小写).

目前,多行块可以在以下结构中使用:

  • 作为 callstatement 语句的 C 表达式;

  • 作为 callprotoargument 语句的 C 类型规范;

  • 作为 usercode 语句的 C 代码块;

  • 作为 pymethoddef 语句的 C 数组列表;

  • 作为文档字符串.

扩展的字符选择器#

F2PY 扩展了字符选择器规范,可以在签名文件或 F2PY 指令中使用,如下所示:

<extended-charselector> := <charselector>
                        | (f2py_len= <len>)

用法请参见 字符串.