签名文件#
接口定义文件 (.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,并且只有当callstatement和callprotoargument对所有入口例程都有效时才能使用它们,等等。
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 函数,用于可选参数的初始化。
例如,如果
usercode在python 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)会禁用optional和required。
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.h和Python.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>)
有关用法,请参阅 字符字符串。