签名文件#

接口定义文件 (.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 [ <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>

where

<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 语句#

  • <use statement> 部分的定义是

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

    where

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

COMMON 块语句#

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

    common / <common name> / <shortentitydecl>
    

    where

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

其他语句#

  • <other statement> 部分指的是上面未描述的任何其他 Fortran 语言构造。F2PY 忽略了其中大部分,但以下除外:

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

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

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

    • implicit 语句
      implicit none
      implicit <list of implicit maps>
      

      where

      <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 设置为 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 数组列表(有关详细信息,请参阅 Extending and Embedding 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) 参数只能是具有正确类型和大小的(Fortran 或 C 意义上的)连续 NumPy 数组。后者与 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 连续数组。

    对于一维数组,无论被包装的函数是 Fortran 还是 C,都没有必要使用 intent(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_<argument name>,其默认值为 0

  • overwrite

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

  • out=<new name>

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

  • 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> 个索引值,并且从 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>)

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