签名文件#
接口定义文件(.pyf)是您可以微调 Python 和 Fortran 之间接口的方式.签名文件( .pyf 文件)的语法规范以 Fortran 90/95 语言规范为模型.几乎所有 Fortran 标准构造都可理解,无论是自由格式还是固定格式(回想一下,Fortran 77 是 Fortran 90/95 的子集).F2PY 对 Fortran 90/95 语言规范引入了一些扩展,这些扩展有助于设计 Fortran 到 Python 的接口,使其更加"Pythonic".
签名文件可能包含任意 Fortran 代码,因此任何 Fortran 90/95 代码都可以被视为签名文件.对于创建接口无关紧要的 Fortran 构造,F2PY 会默默地忽略.但是,这也意味着语法错误不会被 F2PY 捕获,并且只有在构建库时才会被捕获.
备注
目前,F2PY 可能会因某些有效的 Fortran 构造而失败.如果发生这种情况,您可以查看 NumPy GitHub issue tracker 以获取可能的回避方法或正在进行中的想法.
通常,签名文件的内容区分大小写.在扫描 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 expression ;<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参数的函数调用(参见 more details on external arguments );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,只有当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.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>.如果使用了没有
<actual Fortran/C routine name>的fortranname语句,则会生成一个虚拟包装器.usercode <multi-line block>当在
python module块中使用时,给定的 C 代码将被插入到生成的 C/API 源代码中,就在包装函数定义之前.在这里,您可以定义任意 C 函数,用于初始化可选参数.
例如,如果在
python module块中使用了两次usercode,则第二个多行块将在外部例程的定义之后插入.当在
<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>指定(参见entitydecldefinition )备注
默认值必须作为有效的 C 表达式给出.
只要使用
<init_expr>,optional属性就会被 F2PY 自动设置.对于可选的数组参数,其所有维度都必须有界.
required具有此属性的对应参数被认为是强制性的.这是默认设置.只有在需要禁用使用
<init_expr>时的自动optional设置时,才应指定required.如果 Python
None对象被用作必需参数,则该参数将被视为可选参数.也就是说,对于数组参数,会分配内存.如果给出了<init_expr>,则会执行相应的初始化.dimension(<arrayspec>)对应的变量被认为是具有
<arrayspec>中给出的维度的数组.intent(<intentspec>)这指定了相应参数的"意图".
<intentspec>是以下键的逗号分隔列表:in相应的参数被认为是只输入的.这意味着参数的值被传递给 Fortran/C 函数,并且该函数预计不会更改此参数的值.
inout相应的参数被标记为输入/输出或作为就地输出参数.
intent(inout)参数只能是具有适当类型和大小的 contiguous 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(in)或intent(inout),否则使用intent(out)会自动设置intent(hide).默认情况下,返回的多维数组是 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这表示 Fortran/C 函数可能会更改
intent(in)参数的原始内容.F2PY 创建一个可选参数overwrite_<argument name>,默认值为1.
out=<new name>在包装器函数的
__doc__字符串中,将返回的名称替换为<new name>.
callback构造一个外部函数,适用于从 Fortran 调用 Python 函数.必须在相应的
external语句之前指定intent(callback).如果"argument"不在参数列表中,那么它将被添加到 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>;check属性的<C-booleanexpr>;dimension属性的<arrayspec>;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>的 itemsize.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) .
警告
当扫描 Fortran 代码时,F2PY 可能会将 C 表达式转换为小写(请参阅 --[no]-lower 选项).
多行块#
多行块以 ''' (三个单引号) 开始,并以某些严格后续行中的 ''' 结束.多行块只能在 .pyf 文件中使用.多行块的内容可以是任意的(除了不能包含 ''' ),并且不会对其应用任何转换(例如,转换为小写).
目前,多行块可以用于以下构造:
作为
callstatement语句的 C 表达式;作为
callprotoargument语句的 C 类型规范;作为
usercode语句的 C 代码块;作为
pymethoddef语句的 C 数组列表;
作为文档字符串.
扩展的字符选择器#
F2PY 扩展了字符选择器规范,可以在签名文件或 F2PY 指令中使用,如下所示:
<extended-charselector> := <charselector>
| (f2py_len= <len>)
有关用法,请参见 字符串 .