签名文件#

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

F2PY 从 Fortran 例程签名生成一个具有以下签名的 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 语句#

  • <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 名称生成包装器。

      注意

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

F2PY 语句#

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

threadsafe

在对 Fortran/C 函数的调用周围使用 Py_BEGIN_ALLOW_THREADS .. Py_END_ALLOW_THREADS 块。

callstatement <C-expr|multi-line block>

将 F2PY 生成的对 Fortran/C 函数的调用语句替换为 <C-expr|multi-line block>。包装的 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 <多行 代码块>

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

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

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

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

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

pymethoddef <多行 代码块>

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

属性#

F2PY可以使用以下属性。

optional(可选)

相应的参数将移动到 <optional arguments> 列表的末尾。可选参数的默认值可以通过 <init_expr> 指定(参见 entitydecl 定义)。

注意

  • 默认值必须作为有效的C表达式给出。

  • 无论何时使用 <init_expr>,F2PY都会自动设置 optional 属性。

  • 对于可选的数组参数,其所有维度都必须是有界的。

required(必需)

具有此属性的相应参数被认为是必需的。这是默认设置。required 只有在需要禁用当使用 <init_expr> 时自动 optional 设置时才应指定。

如果使用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标量参数传递给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_<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> 可以使用其他参数的值。F2PY 使用 depend(..) 属性提供的信息来确保参数以正确的顺序初始化。如果不使用 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)

警告

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

多行块#

多行块以 '''(三个单引号)开头,并在后续的某一行以 ''' 结尾。多行块只能在 .pyf 文件中使用。多行块的内容可以是任意的(但不能包含 '''),并且不会对其应用任何转换(例如,转换为小写)。

目前,多行块可用于以下结构

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

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

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

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

  • 作为文档字符串。

扩展的字符选择器#

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

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

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