签名文件#

接口定义文件(.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 module 块。 python module 块描述了 F2PY 生成的 Python/C 扩展模块 <modulename>module.c 的内容。

警告

例外:如果 <modulename> 包含子字符串 __user__,则相应的 python module 块描述回调函数的签名(参见 回调参数)。

一个 python module 块具有以下结构

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>] ]

类型声明#

<argument/variable type declaration> 部分的定义为

<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 整数;

如果一个参数没有 <argument type declaration>,则其类型是通过将 implicit 规则应用于其名称来确定的。

语句#

属性语句#

<argument/variable attribute statement><argument/variable type declaration> 类似,但没有 <typespec>

属性语句不能包含其他属性,并且 <entitydecl> 只能是名称列表。有关 F2PY 可使用的属性的更多详细信息,请参阅 属性

使用语句#

  • <use statement> 部分的定义为

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

    其中

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

公共块语句#

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

    common / <common name> / <shortentitydecl>
    

    其中

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

其他语句#

  • <other statement> 部分指的是上面没有描述的任何其他 Fortran 语言结构。F2PY 会忽略其中大部分,除了以下内容

    • call 语句和 external 参数的函数调用(参见 有关 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> )
      

      隐式规则用于确定变量的类型规范(从其名称的首字母),如果变量不是使用 <variable type declaration> 定义的。默认隐式规则由以下内容给出

      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|multi-line block>

<C-expr|multi-line block> 替换 F2PY 生成的对 Fortran/C 函数的调用语句。包装后的 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 [<actual Fortran/C routine name>]

F2PY 允许对给定的 Fortran/C 函数使用任意 <routine name>。然后将此语句用于 <actual Fortran/C routine name>

如果使用 fortranname 语句但没有 <actual Fortran/C routine name>,则会生成一个虚拟包装器。

usercode <multi-line block>

当在 python module 块内使用时,给定的 C 代码将插入到生成的 C/API 源代码中,恰好在包装器函数定义之前。

您可以在此处定义任意 C 函数,用于初始化可选参数。

例如,如果 usercodepython module 块内使用了两次,则第二个多行块将插入到外部例程定义之后。

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

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

pymethoddef <multiline block>

这是一个多行块,它将插入到模块方法 PyMethodDef 数组的定义中。它必须是 C 数组的逗号分隔列表(有关详细信息,请参阅 扩展和嵌入 Python 文档)。 pymethoddef 语句只能在 python module 块内使用。

属性#

以下属性可由 F2PY 使用。

optional

相应的参数将移动到 <optional arguments> 列表的末尾。可选参数的默认值可以通过 <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

    对应参数被标记为输入/输出或就地输出参数。intent(inout) 参数只能是具有正确类型和大小的连续 NumPy 数组(在 Fortran 或 C 意义上)。后者与 NumPy 中使用的默认连续概念一致,并且仅当使用intent(c)时才有效。F2PY 默认假设 Fortran 连续参数。

    注意

    通常不建议使用intent(inout),因为它可能导致意外结果。例如,使用intent(inout)的标量参数被假定为数组对象,以便就地更改有效。请改用intent(in,out)

    另请参见intent(inplace) 属性。

  • inplace

    对应参数被视为输入/输出或就地输出参数。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(hide)optional 属性一起使用时,使用intent(cache) 才有意义。

  • copy

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

  • overwrite

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

  • out=<new name>

    <new name> 替换包装函数的__doc__ 字符串中的返回值名称。

  • callback

    构造一个适合从 Fortran 调用 Python 函数的外部函数。intent(callback) 必须在相应的external 语句之前指定。如果“参数”不在参数列表中,则它将被添加到 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(out,hide),除非指定了intent(in)intent(inout)

  • 如果使用了intent(copy)intent(overwrite),则会引入一个额外的可选参数,其名称为overwrite_<argument name>,默认值为 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__ 模块块中,

  • 或通过<other statements> 块中的演示性(或真实,如果签名文件是真实的 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> 个索引值,该值从0 运行到shape(<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>)

有关用法,请参阅 字符字符串