F2PY-Fortran与Python接口


打包的三种方法 - 入门

使用F2PY将Fortran或C函数包装到Python包含以下步骤:

  • 创建所谓的签名文件,其中包含对Fortran或C函数的包装器的描述,也称为函数的签名。对于Fortran例程,F2PY可以通过扫描Fortran源代码并捕获创建包装函数所需的所有相关信息来创建初始签名文件。
  • 可选地,可以编辑F2PY创建的签名文件以优化包装器功能,使它们“更智能”和更“Pythonic”。
  • F2PY读取签名文件并编写包含Fortran / C / Python绑定的Python C / API模块。
  • F2PY编译所有源并构建包含包装器的扩展模块。在构建扩展模块时,F2PY使用 numpy_distutils它支持许多Fortran 77/90/95编译器,包括Gnu,Intel,Sun Fortre,SGI MIPSpro,Absoft,NAG,Compaq等编译器。

根据具体情况,这些步骤可以通过一个命令或一步一步执行,一些步骤可以省略或与其他步骤组合。

下面我将描述使用F2PY的三种典型方法。以下示例 Fortran 77代码 将说明:

C FILE: FIB1.F
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE FIB1.F

快捷的方式

将Fortran子例程包装FIB到Python 的最快方法是运行

python -m numpy.f2py -c fib1.f -m fib1

此命令构建(参见-cflag,不带参数执行以查看命令行选项的说明)扩展模块(请参阅标志)到当前目录。现在,在Python中,可以通过以下方式访问Fortran子例程:python -m numpy.f2pyfib1.so-mFIBfib1.fib

>>> import numpy
>>> import fib1
>>> print fib1.fib.__doc__
fib - Function signature:
  fib(a,[n])
Required arguments:
  a : input rank-1 array('d') with bounds (n)
Optional arguments:
  n := len(a) input int

>>> a = numpy.zeros(8,'d')
>>> fib1.fib(a)
>>> print a
[  0.   1.   1.   2.   3.   5.   8.  13.]

注意

  • 请注意,F2PY发现第二个参数n是第一个数组参数的维度a。由于默认情况下所有参数都是仅输入参数,因此F2PY n可以使用默认值作为可选参数len(a)
  • 可以使用不同的值来选择n
>>> a1 = numpy.zeros(8,'d')
>>> fib1.fib(a1,6)
>>> print a1
[ 0.  1.  1.  2.  3.  5.  0.  0.]

但是当它与输入数组不兼容时会引发异常a

>>> fib1.fib(a,10)
fib:n=10
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
fib.error: (len(a)>=n) failed for 1st keyword n
>>>

这展示了F2PY中的一个有用功能,即F2PY实现相关参数之间的基本兼容性检查,以避免任何意外崩溃。

  • 当一个NumPy数组(即Fortran连续且具有与假定的Fortran类型相对应的dtype)用作输入数组参数时,其C指针将直接传递给Fortran。

否则,F2PY会生成输入数组的连续副本(具有正确的dtype),并将副本的C指针传递给Fortran子例程。因此,对输入数组(副本)的任何可能更改都不会影响原始参数,如下所示:

>>> a = numpy.ones(8,'i')
>>> fib1.fib(a)
>>> print a
[1 1 1 1 1 1 1 1]

显然,这不是预期的行为。上述示例使用的事实dtype=float被认为是偶然的。

F2PY提供intent(inplace)了将修改输入数组属性的属性,以便Fortran例程所做的任何更改也将在输入参数中生效。例如,如果指定(见下文,如何),则上面的示例将为:intent(inplace) a

>>> a = numpy.ones(8,'i')
>>> fib1.fib(a)
>>> print a
[  0.   1.   1.   2.   3.   5.   8.  13.]

但是,将Fortran子例程所做的更改返回到python的推荐方法是使用intent(out)属性。它更有效,更清洁。

  • fib1.fibPython中的用法与FIB在Fortran中使用非常相似 。但是,在Python中使用 原位 输出参数表明样式很差,因为Python中没有关于错误参数类型的安全机制。使用Fortran或C时,编译器自然会在编译期间发现任何类型不匹配,但在Python中,必须在运行时检查类型。因此,在Python中使用 原位 输出参数可能会导致难以发现错误,更不用说在实现所有必需的类型检查时代码将不太可读。

虽然将Fortran例程包装到Python的演示方法非常简单,但它有几个缺点(参见上面的注释)。这些缺点是由于F2PY无法确定一个或另一个参数的实际意图,输入或输出参数,或两者,或其他东西。因此,F2PY保守地假定所有参数都是默认的输入参数。

但是,有一些方法(见下文)如何“教导”F2PY关于函数参数的真实意图(以及其他内容); 然后F2PY能够为Fortran函数生成更多Pythonic(更明确,更易于使用,更不容易出错)的包装器。

聪明的方式

让我们逐个应用将Fortran函数包装到Python的步骤。

  • 首先,我们fib1.f通过运行创建一个签名文件

    python -m numpy.f2py fib1.f -m fib2 -h fib1.pyf

    签名文件保存到fib1.pyf(见-h标志),其内容如下所示。

    !    -*- f90 -*-
    python module fib2 ! in 
        interface  ! in :fib2
            subroutine fib(a,n) ! in :fib2:fib1.f
                real*8 dimension(n) :: a
                integer optional,check(len(a)>=n),depend(a) :: n=len(a)
            end subroutine fib
        end interface 
    end python module fib2
    
    ! This file was auto-generated with f2py (version:2.28.198-1366).
    ! See http://cens.ioc.ee/projects/f2py2e/
  • 接下来,我们将教导F2PY参数n是一个输入参数(use intent(in)属性),结果,即a调用Fortran函数后的内容FIB,应该返回给Python(use intent(out)属性)。此外,a应使用input参数给出的大小动态创建数组n(use depend(n)属性表示依赖关系)。

    修改后的版本fib1.pyf(保存为 fib2.pyf)的内容如下:

    !    -*- f90 -*-
    python module fib2 
        interface
            subroutine fib(a,n)
                real*8 dimension(n),intent(out),depend(n) :: a
                integer intent(in) :: n
            end subroutine fib
        end interface 
    end python module fib2
  • 最后,我们通过运行构建扩展模块

    python -m numpy.f2py -c fib2.pyf fib1.f

在Python中:

>>> import fib2
>>> print fib2.fib.__doc__
fib - Function signature:
  a = fib(n)
Required arguments:
  n : input int
Return objects:
  a : rank-1 array('d') with bounds (n)

>>> print fib2.fib(8)
[  0.   1.   1.   2.   3.   5.   8.  13.]

注意

  • 显然,fib2.fib现在的签名FIB更接近Fortran子程序的意图:给定数字nfib2.fib将第一个nFibonacci数作为NumPy数组返回。此外,新的Python签名fib2.fib 排除了我们遇到的任何意外fib1.fib
  • 请注意,默认情况下使用single intent(out)也意味着 intent(hide)。具有intent(hide)指定属性的参数将不会列在包装函数的参数列表中。

快捷而聪明的方式

如上所述,包装Fortran函数的“智能方法”适用于包装(例如第三方)Fortran代码,对其源代码的修改是不可取的,甚至也不可能。

但是,如果编辑Fortran代码是可以接受的,则在大多数情况下可以跳过生成中间签名文件。即,可以使用所谓的F2PY指令将F2PY特定属性直接插入到Fortran源代码中。F2PY指令定义了特殊注释行(Cf2py例如,从Fortran编译器开始),但是F2PY将它们解释为普通行。

下面显示了示例Fortran代码的修改版本,保存为fib3.f

C FILE: FIB3.F
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
Cf2py intent(in) n
Cf2py intent(out) a
Cf2py depend(n) a
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE FIB3.F

现在可以在一个命令中执行构建扩展模块:

python -m numpy.f2py -c -m fib3 fib3.f

请注意,生成的包装器与FIB前一种情况一样“智能”:

>>> import fib3
>>> print fib3.fib.__doc__
fib - Function signature:
  a = fib(n)
Required arguments:
  n : input int
Return objects:
  a : rank-1 array('d') with bounds (n)

>>> print fib3.fib(8)
[  0.   1.   1.   2.   3.   5.   8.  13.]

签名文件

签名文件(.pyf文件)的语法规范借鉴了Fortran 90/95语言规范。几乎所有的Fortran 90/95标准结构都以免费和固定格式被理解(回想一下Fortran 77是Fortran 90/95的子集)。F2PY 还引入了Fortran 90/95语言规范的一些扩展,有助于将Fortran设计为Python接口,使其更加“Pythonic”。

签名文件可能包含任意Fortran代码(因此Fortran代码可以被视为签名文件)。F2PY 默默地忽略与创建接口无关的Fortran构造。但是,这也包括语法错误。所以,小心不要制作;-)。

通常,签名文件的内容区分大小写。扫描Fortran代码并写入签名文件时,除多行块或使用 --no-lower 选项外,F2PY 会自动降低所有情况。

签名文件的语法如下所示。

Python模块块

签名文件可能包含一个(推荐)或更多 python模块 块。 python模块 块描述了F2PY生成的Python / C扩展模块 module.c的内容。

例外:如果 <modulename> 包含子字符串 __user__ ,则相应的 python模块 块描述所谓的回调函数的签名。

python模块块具有以下结构:

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> is a comma separated list of attributes;
  • <arrayspec> is a comma separated list of dimension bounds;
  • <init_expr> is a C expression.
  • <intlen> may be negative integer for integer type specifications. In such cases integer* represents unsigned C integers.

如果参数没有 <argument type Declaration>,则通过对其名称应用隐式(implicit)规则来确定其类型。

声明

  • 属性声明:

    The <argument/variable attribute statement> is <argument/variable type declaration> without <typespec>. In addition, in an attribute statement one cannot use other attributes, also <entitydecl> can be only a list of names.

  • 使用声明:

    The definition of the <use statement> part is

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

    where

    <rename_list> := <local_name> => <use_name> [ , <rename_list> ]

    Currently F2PY uses use statement only for linking call-back modules and external arguments (call-back functions).

  • Common block statements:

    The definition of the <common block statement> part is

    common / <common name> / <shortentitydecl>

    where

    <shortentitydecl> := <name> [ ( <arrayspec> ) ] [ , <shortentitydecl> ]

    If a python module block contains two or more common blocks with the same name, the variables from the additional declarations are appended. The types of variables in <shortentitydecl> are defined using <argument type declarations>. Note that the corresponding <argument type declarations> may contain array specifications; then you don’t need to specify these in <shortentitydecl>.

  • Other statements:

    The <other statement> part refers to any other Fortran language constructs that are not described above. F2PY ignores most of them except

    • call statements and function calls of external arguments;

    • include statements

      include '<filename>'
      include "<filename>"

      If a file <filename> does not exist, the include statement is ignored. Otherwise, the file <filename> is included to a signature file. include statements can be used in any part of a signature file, also outside the Fortran/C routine signature blocks.

    • implicit statements

      implicit none
      implicit <list of implicit maps>

      where

      <implicit map> := <typespec> ( <list of letters or range of letters> )

      Implicit rules are used to determine the type specification of a variable (from the first-letter of its name) if the variable is not defined using <variable type declaration>. Default implicit rule is given by

      implicit real (a-h,o-z,$_), integer (i-m)
    • entry statements

      entry <entry name> [([<arguments>])]

      F2PY generates wrappers to all entry names using the signature of the routine block.

      Tip: entry statement can be used to describe the signature of an arbitrary routine allowing F2PY to generate a number of wrappers from only one routine block signature. There are few restrictions while doing this: fortranname cannot be used, callstatement and callprotoargument can be used only if they are valid for all entry routines, etc.

    In addition, F2PY introduces the following statements:

    • threadsafe

      Use Py_BEGIN_ALLOW_THREADS .. Py_END_ALLOW_THREADS block around the call to Fortran/C function.

    • callstatement <C-expr|multi-line block>

      Replace F2PY generated call statement to Fortran/C function with <C-expr|multi-line block>. The wrapped Fortran/C function is available as (*f2py_func). To raise an exception, set f2py_success = 0 in <C-expr|multi-line block>.

    • callprotoargument <C-typespecs>

      When callstatement statement is used then F2PY may not generate proper prototypes for Fortran/C functions (because <C-expr> may contain any function calls and F2PY has no way to determine what should be the proper prototype). With this statement you can explicitly specify the arguments of the corresponding prototype:

      extern <return type> FUNC_F(<routine name>,<ROUTINE NAME>)(<callprotoargument>);
    • fortranname [<actual Fortran/C routine name>]

      You can use arbitrary <routine name> for a given Fortran/C function. Then you have to specify <actual Fortran/C routine name> with this statement.

      If fortranname statement is used without <actual Fortran/C routine name> then a dummy wrapper is generated.

    • usercode

      When used inside python module block, then given C code will be inserted to generated C/API source just before wrapper function definitions. Here you can define arbitrary C functions to be used in initialization of optional arguments, for example. If usercode is used twice inside python module block then the second multiline block is inserted after the definition of external routines.

      When used inside <routine signature>, then given C code will be inserted to the corresponding wrapper function just after declaring variables but before any C statements. So, usercode follow-up can contain both declarations and C statements.

      When used inside the first interface block, then given C code will be inserted at the end of the initialization function of the extension module. Here you can modify extension modules dictionary. For example, for defining additional variables etc.

    • pymethoddef <multiline block>

      Multiline block will be inserted to the definition of module methods PyMethodDef-array. It must be a comma-separated list of C arrays (see Extending and Embedding Python documentation for details). pymethoddef statement can be used only inside python module block.

属性

The following attributes are used by F2PY:

  • optional

    The corresponding argument is moved to the end of <optional arguments> list. A default value for an optional argument can be specified <init_expr>, see entitydecl definition. Note that the default value must be given as a valid C expression.

    Note that whenever <init_expr> is used, optional attribute is set automatically by F2PY.

    For an optional array argument, all its dimensions must be bounded.

  • required

    The corresponding argument is considered as a required one. This is default. You need to specify required only if there is a need to disable automatic optional setting when <init_expr> is used.

    If Python None object is used as a required argument, the argument is treated as optional. That is, in the case of array argument, the memory is allocated. And if <init_expr> is given, the corresponding initialization is carried out.

  • dimension(<arrayspec>)

    The corresponding variable is considered as an array with given dimensions in <arrayspec>.

  • intent(<intentspec>)

    This specifies the “intention” of the corresponding argument. <intentspec> is a comma separated list of the following keys:

    • in

      The argument is considered as an input-only argument. It means that the value of the argument is passed to Fortran/C function and that function is expected not to change the value of an argument.

    • inout

      The argument is considered as an input/output or in situ output argument. intent(inout) arguments can be only “contiguous” NumPy arrays with proper type and size. Here “contiguous” can be either in Fortran or C sense. The latter one coincides with the contiguous concept used in NumPy and is effective only if intent(c) is used. Fortran contiguity is assumed by default.

      Using intent(inout) is generally not recommended, use intent(in,out) instead. See also intent(inplace) attribute.

    • inplace

      The argument is considered as an input/output or in situ output argument. intent(inplace) arguments must be NumPy arrays with proper size. If the type of an array is not “proper” or the array is non-contiguous then the array will be changed in-place to fix the type and make it contiguous.

      Using intent(inplace) is generally not recommended either. For example, when slices have been taken from an intent(inplace) argument then after in-place changes, slices data pointers may point to unallocated memory area.

    • out

      The argument is considered as a return variable. It is appended to the <returned variables> list. Using intent(out) sets intent(hide) automatically, unless also intent(in) or intent(inout) were used.

      By default, returned multidimensional arrays are Fortran-contiguous. If intent(c) is used, then returned multidimensional arrays are C-contiguous.

    • hide

      The argument is removed from the list of required or optional arguments. Typically intent(hide) is used with intent(out) or when <init_expr> completely determines the value of the argument like in the following example:

      integer intent(hide),depend(a) :: n = len(a)
      real intent(in),dimension(n) :: a
    • c

      The argument is treated as a C scalar or C array argument. In the case of a scalar argument, its value is passed to C function as a C scalar argument (recall that Fortran scalar arguments are actually C pointer arguments). In the case of an array argument, the wrapper function is assumed to treat multidimensional arrays as C-contiguous arrays.

      There is no need to use intent(c) for one-dimensional arrays, no matter if the wrapped function is either a Fortran or a C function. This is because the concepts of Fortran- and C contiguity overlap in one-dimensional cases.

      If intent(c) is used as a statement but without an entity declaration list, then F2PY adds the intent(c) attribute to all arguments.

      Also, when wrapping C functions, one must use intent(c) attribute for <routine name> in order to disable Fortran specific F_FUNC(..,..) macros.

    • cache

      The argument is treated as a junk of memory. No Fortran nor C contiguity checks are carried out. Using intent(cache) makes sense only for array arguments, also in connection with intent(hide) or optional attributes.

    • copy

      Ensure that the original contents of intent(in) argument is preserved. Typically used in connection with intent(in,out) attribute. F2PY creates an optional argument overwrite_ with the default value 0.

    • overwrite

      The original contents of the intent(in) argument may be altered by the Fortran/C function. F2PY creates an optional argument overwrite_ with the default value 1.

    • out=

      Replace the return name with <new name> in the __doc__ string of a wrapper function.

    • callback

      Construct an external function suitable for calling Python function from Fortran. intent(callback) must be specified before the corresponding external statement. If ‘argument’ is not in argument list then it will be added to Python wrapper but only initializing external function.

      Use intent(callback) in situations where a Fortran/C code assumes that a user implements a function with given prototype and links it to an executable. Don’t use intent(callback) if function appears in the argument list of a Fortran routine.

      With intent(hide) or optional attributes specified and using a wrapper function without specifying the callback argument in argument list then call-back function is looked in the namespace of F2PY generated extension module where it can be set as a module attribute by a user.

    • aux

      Define auxiliary C variable in F2PY generated wrapper function. Useful to save parameter values so that they can be accessed in initialization expression of other variables. Note that intent(aux) silently implies intent(c).

    The following rules apply:

    • If no intent(in | inout | out | hide) is specified, intent(in) is assumed.
    • intent(in,inout) is intent(in).
    • intent(in,hide) or intent(inout,hide) is intent(hide).
    • intent(out) is intent(out,hide) unless intent(in) or intent(inout) is specified.
    • If intent(copy) or intent(overwrite) is used, then an additional optional argument is introduced with a name overwrite_ and a default value 0 or 1, respectively.
    • intent(inout,inplace) is intent(inplace).
    • intent(in,inplace) is intent(inplace).
    • intent(hide) disables optional and required.
  • check([<C-booleanexpr>])

    Perform consistency check of arguments by evaluating <C-booleanexpr>; if <C-booleanexpr> returns 0, an exception is raised.

    If check(..) is not used then F2PY generates few standard checks (e.g. in a case of an array argument, check for the proper shape and size) automatically. Use check() to disable checks generated by F2PY.

  • depend([<names>])

    This declares that the corresponding argument depends on the values of variables in the list <names>. For example, <init_expr> may use the values of other arguments. Using information given by depend(..) attributes, F2PY ensures that arguments are initialized in a proper order. If depend(..) attribute is not used then F2PY determines dependence relations automatically. Use depend() to disable dependence relations generated by F2PY.

    When you edit dependence relations that were initially generated by F2PY, be careful not to break the dependence relations of other relevant variables. Another thing to watch out is cyclic dependencies. F2PY is able to detect cyclic dependencies when constructing wrappers and it complains if any are found.

  • allocatable

    The corresponding variable is Fortran 90 allocatable array defined as Fortran 90 module data.

  • external

    The corresponding argument is a function provided by user. The signature of this so-called call-back function can be defined

    • in __user__ module block,
    • or by demonstrative (or real, if the signature file is a real Fortran code) call in the <other statements> block.

    For example, F2PY generates from

    external cb_sub, cb_fun
    integer n
    real a(n),r
    call cb_sub(a,n)
    r = cb_fun(4)

    the following call-back signatures:

    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

    The corresponding user-provided Python function are then:

    def cb_sub(a,[n]):
        ...
        return
    def cb_fun(e_4_e):
        ...
        return r

    See also intent(callback) attribute.

  • parameter

    The corresponding variable is a parameter and it must have a fixed value. F2PY replaces all parameter occurrences by their corresponding values.

扩展

F2PY 指令

The so-called F2PY directives allow using F2PY signature file constructs also in Fortran 77/90 source codes. With this feature you can skip (almost) completely intermediate signature file generations and apply F2PY directly to Fortran source codes.

F2PY directive has the following form:

<comment char>f2py ...

where allowed comment characters for fixed and free format Fortran codes are cC*!# and !, respectively. Everything that follows f2py is ignored by a compiler but read by F2PY as a normal Fortran, non-comment line:

When F2PY finds a line with F2PY directive, the directive is first replaced by 5 spaces and then the line is reread.

For fixed format Fortran codes, <comment char> must be at the first column of a file, of course. For free format Fortran codes, F2PY directives can appear anywhere in a file.

C 表达式

C expressions are used in the following parts of signature files:

  • <init_expr> of variable initialization;
  • <C-booleanexpr> of the check attribute;
  • <arrayspec> of the dimension attribute;
  • callstatement statement, here also a C multiline block can be used.

A C expression may contain:

  • standard C constructs;
  • functions from math.h and Python.h;
  • variables from the argument list, presumably initialized before

according to given dependence relations;

  • the following CPP macros:
    • rank(<name>) Returns the rank of an array <name>.
    • shape(<name>,<n>) Returns the <n-th dimension of an array <name>.
    • len(<name>) Returns the length of an array <name>.
    • size(<name>) Returns the size of an array <name>.
    • slen(<name>) Returns the length of a string <name>.

For initializing an array <array name>, F2PY generates a loop over all indices and dimensions that executes the following pseudo-statement:

<array name>(_i[0],_i[1],...) = <init_expr>;

where _i[<i>] refers to the <i>-th index value and that runs from 0 to shape(<array name>,<i>)-1.

For example, a function myrange(n) generated from the following signature

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

is equivalent to numpy.arange(n,dtype=float).

Warning

F2PY may lower cases also in C expressions when scanning Fortran codes (see --[no]-lower option).

多行块

A multiline block starts with ''' (triple single-quotes) and ends with ''' in some strictly subsequent line. Multiline blocks can be used only within .pyf files. The contents of a multiline block can be arbitrary (except that it cannot contain ''') and no transformations (e.g. lowering cases) are applied to it.

Currently, multiline blocks can be used in the following constructs:

  • as a C expression of the callstatement statement;
  • as a C type specification of the callprotoargument statement;
  • as a C code block of the usercode statement;
  • as a list of C arrays of the pymethoddef statement;
  • as documentation string.

在Python中使用F2PY构建

Fortran / C例程,公共块或F2PY生成的Fortran 90模块数据的所有包装器都作为fortran 类型对象公开给Python 。 例程包装器是可调用fortran类型对象,而Fortran数据包装器具有引用数据对象的属性。

所有fortran类型对象都具有_cpointer包含CObject的属性,该CObject引用相应Fortran / C函数的C指针或C级别的变量。当这些函数的计算部分在C或Fortran中实现并用F2PY(或任何其他工具)包装时,这些CObject可以用作F2PY生成函数的回调参数,以绕过从Fortran或C调用Python函数的Python C / API层。能够提供功能的CObject)。

考虑一个Fortran 77文件ftype.f

C FILE: FTYPE.F
      SUBROUTINE FOO(N)
      INTEGER N
Cf2py integer optional,intent(in) :: n = 13
      REAL A,X
      COMMON /DATA/ A,X(3)
      PRINT*, "IN FOO: N=",N," A=",A," X=[",X(1),X(2),X(3),"]"
      END
C END OF FTYPE.F

并使用。构建一个包装器。f2py -c ftype.f -m ftype

在Python中:

>>> import ftype
>>> print ftype.__doc__
This module 'ftype' is auto-generated with f2py (version:2.28.198-1366).
Functions:
  foo(n=13)
COMMON blocks:
  /data/ a,x(3)
.
>>> type(ftype.foo),type(ftype.data)
(<type 'fortran'>, <type 'fortran'>)
>>> ftype.foo()
 IN FOO: N= 13 A=  0. X=[  0.  0.  0.]
>>> ftype.data.a = 3
>>> ftype.data.x = [1,2,3]
>>> ftype.foo()
 IN FOO: N= 13 A=  3. X=[  1.  2.  3.]
>>> ftype.data.x[1] = 45  
>>> ftype.foo(24)
 IN FOO: N= 24 A=  3. X=[  1.  45.  3.]
>>> ftype.data.x
array([  1.,  45.,   3.],'f')

标量参数

通常,F2PY生成的包装函数的标量参数可以是普通的Python标量(整数,浮点数,复数)以及标量的任意序列对象(列表,元组,数组,字符串)。在后一种情况下,序列对象的第一个元素作为标量参数传递给Fortran例程。

请注意,当需要进行类型转换并且可能丢失信息时(例如,当将类型转换浮动为整数或复数转换为浮动时),F2PY不会引发任何异常。在复杂到真实的类型转换中,仅使用复数的实部。

intent(inout)标量参数被假定为数组对象,以便 原位 更改生效。建议使用具有适当类型的数组,但也可以使用其他类型的数组。

考虑以下Fortran 77代码:

C FILE: SCALAR.F
      SUBROUTINE FOO(A,B)
      REAL*8 A, B
Cf2py intent(in) a
Cf2py intent(inout) b
      PRINT*, "    A=",A," B=",B
      PRINT*, "INCREMENT A AND B"
      A = A + 1D0
      B = B + 1D0
      PRINT*, "NEW A=",A," B=",B
      END
C END OF FILE SCALAR.F

并使用它包装。f2py -c -m scalar scalar.f

在Python中:

>>> import scalar
>>> print scalar.foo.__doc__
foo - Function signature:
  foo(a,b)
Required arguments:
  a : input float
  b : in/output rank-0 array(float,'d')

>>> scalar.foo(2,3)   
     A=  2. B=  3.
 INCREMENT A AND B
 NEW A=  3. B=  4.
>>> import numpy
>>> a=numpy.array(2)   # these are integer rank-0 arrays
>>> b=numpy.array(3)
>>> scalar.foo(a,b)
     A=  2. B=  3.
 INCREMENT A AND B
 NEW A=  3. B=  4.
>>> print a,b            # note that only b is changed in situ
2 4

字符串参数

F2PY生成的包装函数接受(几乎)任何Python对象作为字符串参数,str应用于非字符串对象。例外是NumPy数组必须具有类型代码'c''1'用作字符串参数。

当将字符串用作F2PY生成的包装函数的字符串参数时,字符串可以具有任意长度。如果长度大于预期,则字符串将被截断。如果长度小于预期,则分配并填充额外的内存\0

因为Python字符串是不可变的,所以intent(inout)参数需要字符串的数组版本才能使 原位 更改生效。

考虑以下Fortran 77代码:

C FILE: STRING.F
      SUBROUTINE FOO(A,B,C,D)
      CHARACTER*5 A, B
      CHARACTER*(*) C,D
Cf2py intent(in) a,c
Cf2py intent(inout) b,d
      PRINT*, "A=",A
      PRINT*, "B=",B
      PRINT*, "C=",C
      PRINT*, "D=",D
      PRINT*, "CHANGE A,B,C,D"
      A(1:1) = 'A'
      B(1:1) = 'B'
      C(1:1) = 'C'
      D(1:1) = 'D'
      PRINT*, "A=",A
      PRINT*, "B=",B
      PRINT*, "C=",C
      PRINT*, "D=",D
      END
C END OF FILE STRING.F

并使用它包装。f2py -c -m mystring string.f

Python会话:

>>> import mystring
>>> print mystring.foo.__doc__
foo - Function signature:
  foo(a,b,c,d)
Required arguments:
  a : input string(len=5)
  b : in/output rank-0 array(string(len=5),'c')
  c : input string(len=-1)
  d : in/output rank-0 array(string(len=-1),'c')

>>> import numpy
>>> a=numpy.array('123')
>>> b=numpy.array('123')
>>> c=numpy.array('123')
>>> d=numpy.array('123')
>>> mystring.foo(a,b,c,d)
 A=123
 B=123
 C=123
 D=123
 CHANGE A,B,C,D
 A=A23
 B=B23
 C=C23
 D=D23
>>> a.tostring(),b.tostring(),c.tostring(),d.tostring()
('123', 'B23', '123', 'D23')

数组参数

通常,F2PY生成的包装函数的数组参数接受可以转换为NumPy数组对象的任意序列。一个例外是intent(inout)数组参数,它们必须始终是正确连续的并且具有正确的类型,否则会引发异常。另一个例外是intent(inplace)数组参数,如果参数的类型与预期不同,则属性将在原位更改(intent(inplace)有关更多信息,请参阅属性)。

通常,如果NumPy数组是正确连续的并且具有适当的类型,那么它将直接传递给包装的Fortran / C函数。否则,将生成输入数组的元素副本,并将正确连续且具有适当类型的副本用作数组参数。

有两种类型的适当连续的NumPy数组:

  • 当数据按列存储时,Fortran连续数组,即存储在存储器中的数据索引从最低维开始;
  • 当数据以行方式存储时,C-连续或简单连续的数组,即存储在存储器中的数据的索引从最高维度开始。

对于一维数组,这些概念重合。

例如,如果2x2数组A的元素按以下顺序存储在内存中,则它是Fortran连续的:

A[0,0] A[1,0] A[0,1] A[1,1]

如果订单如下,则为C-contiguous:

A[0,0] A[0,1] A[1,0] A[1,1]

要测试数组是否为C-contiguous,请使用.iscontiguous() NumPy数组的方法。为了测试Fortran连续性,所有F2PY生成的扩展模块都提供了一个功能 has_column_major_storage()。此功能相当于 .flags.f_contiguous但效率更高。

通常不需要担心数组如何存储在内存中以及包装函数(Fortran函数还是C函数)是否采用一个或另一个存储顺序。F2PY自动确保包装函数以适当的存储顺序获取参数; 相应的算法被设计为仅在绝对必要时才复制数组。但是,当处理大小接近计算机中物理内存大小的非常大的多维输入数组时,必须注意始终使用适当连续且正确的类型参数。

要在将输入数组传递给Fortran例程之前将其转换为列主存储顺序,请使用as_column_major_storage()由所有F2PY生成的扩展模块提供的函数 。

考虑Fortran 77代码:

C FILE: ARRAY.F
      SUBROUTINE FOO(A,N,M)
C
C     INCREMENT THE FIRST ROW AND DECREMENT THE FIRST COLUMN OF A
C
      INTEGER N,M,I,J
      REAL*8 A(N,M)
Cf2py intent(in,out,copy) a
Cf2py integer intent(hide),depend(a) :: n=shape(a,0), m=shape(a,1)
      DO J=1,M
         A(1,J) = A(1,J) + 1D0
      ENDDO
      DO I=1,N
         A(I,1) = A(I,1) - 1D0
      ENDDO
      END
C END OF FILE ARRAY.F

并使用它包装。f2py -c -m arr array.f -DF2PY_REPORT_ON_ARRAY_COPY=1

在Python中:

>>> import arr
>>> from numpy import array
>>> print arr.foo.__doc__
foo - Function signature:
  a = foo(a,[overwrite_a])
Required arguments:
  a : input rank-2 array('d') with bounds (n,m)
Optional arguments:
  overwrite_a := 0 input int
Return objects:
  a : rank-2 array('d') with bounds (n,m)

>>> a=arr.foo([[1,2,3],
...            [4,5,6]])
copied an array using PyArray_CopyFromObject: size=6, elsize=8
>>> print a
[[ 1.  3.  4.]
 [ 3.  5.  6.]]
>>> a.iscontiguous(), arr.has_column_major_storage(a)
(0, 1)
>>> b=arr.foo(a)              # even if a is proper-contiguous
...                           # and has proper type, a copy is made
...                           # forced by intent(copy) attribute
...                           # to preserve its original contents
... 
copied an array using copy_ND_array: size=6, elsize=8
>>> print a
[[ 1.  3.  4.]
 [ 3.  5.  6.]]
>>> print b
[[ 1.  4.  5.]
 [ 2.  5.  6.]]
>>> b=arr.foo(a,overwrite_a=1) # a is passed directly to Fortran
...                            # routine and its contents is discarded
... 
>>> print a
[[ 1.  4.  5.]
 [ 2.  5.  6.]]
>>> print b
[[ 1.  4.  5.]
 [ 2.  5.  6.]]
>>> a is b                       # a and b are actually the same objects
1
>>> print arr.foo([1,2,3])       # different rank arrays are allowed
copied an array using PyArray_CopyFromObject: size=3, elsize=8
[ 1.  1.  2.]
>>> print arr.foo([[[1],[2],[3]]])
copied an array using PyArray_CopyFromObject: size=3, elsize=8
[ [[ 1.]
  [ 3.]
  [ 4.]]]
>>>
>>> # Creating arrays with column major data storage order:
...
>>> s = arr.as_column_major_storage(array([[1,2,3],[4,5,6]]))
copied an array using copy_ND_array: size=6, elsize=4
>>> arr.has_column_major_storage(s)
1
>>> print s
[[1 2 3]
 [4 5 6]]
>>> s2 = arr.as_column_major_storage(s)
>>> s2 is s    # an array with column major storage order 
               # is returned immediately
1

回调参数

F2PY支持从Fortran或C代码调用Python函数。

考虑以下Fortran 77代码:

C FILE: CALLBACK.F
      SUBROUTINE FOO(FUN,R)
      EXTERNAL FUN
      INTEGER I
      REAL*8 R
Cf2py intent(out) r
      R = 0D0
      DO I=-5,5
         R = R + FUN(I)
      ENDDO
      END
C END OF FILE CALLBACK.F

并使用它包装。f2py -c -m callback callback.f

在Python中:

>>> import callback
>>> print callback.foo.__doc__
foo - Function signature:
  r = foo(fun,[fun_extra_args])
Required arguments:
  fun : call-back function
Optional arguments:
  fun_extra_args := () input tuple
Return objects:
  r : float
Call-back functions:
  def fun(i): return r
  Required arguments:
    i : input int
  Return objects:
    r : float

>>> def f(i): return i*i
... 
>>> print callback.foo(f)     
110.0
>>> print callback.foo(lambda i:1)
11.0

在上面的示例中,F2PY能够准确地猜测回叫函数的签名。但是,有时F2PY无法按照人们的意愿建立签名,然后必须手动修改签名文件中的回调函数的签名。即,签名文件可以包含特殊模块(这些模块的名称包含子串__user__),其收集回调函数的各种签名。例程签名中的回调参数具有属性external(另请参见intent(callback)属性)。要在__user__模块块中关联回调参数及其签名,请使用use如下所示的语句。回调参数的相同签名可以在不同的例程签名中引用。

我们使用与前面示例相同的Fortran 77代码,但现在我们假装F2PY无法正确猜测回调参数的签名。首先,我们callback2.pyf使用F2PY 创建一个初始签名文件:

f2py -m callback2 -h callback2.pyf callback.f

然后按如下方式修改它

!    -*- f90 -*-
python module __user__routines 
    interface
        function fun(i) result (r)
            integer :: i
            real*8 :: r
        end function fun
    end interface
end python module __user__routines

python module callback2
    interface
        subroutine foo(f,r)
            use __user__routines, f=>fun
            external f
            real*8 intent(out) :: r
        end subroutine foo
    end interface 
end python module callback2

最后,使用构建扩展模块。f2py -c callback2.pyf callback.f

示例Python会话与前面的示例相同,只是参数名称会有所不同。

有时,Fortran程序包可能要求用户提供程序包将使用的例程。F2PY可以构造这种例程的接口,以便可以从Fortran调用Python函数。

考虑以下Fortran 77子例程,它接受一个数组并将一个函数func应用于其元素。

subroutine calculate(x,n)
cf2py intent(callback) func
      external func
c     The following lines define the signature of func for F2PY:
cf2py real*8 y
cf2py y = func(y)
c
cf2py intent(in,out,copy) x
      integer n,i
      real*8 x(n)
      do i=1,n
         x(i) = func(x(i))
      end do
      end

预计功能func已在外部定义。为了使用Python函数func,它必须具有一个属性intent(callback)(必须在external语句之前指定)。

最后,使用构建扩展模块 f2py -c -m foo calculate.f

在Python中:

>>> import foo
>>> foo.calculate(range(5), lambda x: x*x)
array([  0.,   1.,   4.,   9.,  16.])
>>> import math
>>> foo.calculate(range(5), math.exp)
array([  1.        ,   2.71828175,   7.38905621,  20.08553696,  54.59814835])

该函数作为参数包含在对Fortran子例程的python函数调用中,即使它 不在 Fortran子例程参数列表中。“外部”是指由f2py生成的C函数,而不是python函数本身。必须将python函数提供给C函数。

回调函数也可以在模块中明确设置。然后,没有必要将参数列表中的函数传递给Fortran函数。如果调用python回调函数的Fortran函数本身由另一个Fortran函数调用,则可能需要这样做。

考虑以下Fortran 77子例程:

subroutine f1()
         print *, "in f1, calling f2 twice.."
         call f2()
         call f2()
         return
      end

      subroutine f2()
cf2py    intent(callback, hide) fpy
         external fpy
         print *, "in f2, calling f2py.."
         call fpy()
         return
      end

并使用它包装。f2py -c -m pfromf extcallback.f

在Python中:

>>> import pfromf
>>> pfromf.f2()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
pfromf.error: Callback fpy not defined (as an argument or module pfromf attribute).

>>> def f(): print "python f"
... 
>>> pfromf.fpy = f
>>> pfromf.f2()
 in f2, calling f2py..
python f
>>> pfromf.f1()
 in f1, calling f2 twice..
 in f2, calling f2py..
python f
 in f2, calling f2py..
python f
>>>

解决回调函数的参数

F2PY生成的接口在回调参数方面非常灵活。对于每个回调参数_extra_args,F2PY引入了另一个可选参数。此参数可用于将额外参数传递给用户提供的回调参数。

如果F2PY生成的包装函数需要以下回调参数:

def fun(a_1,...,a_n):
   ...
   return x_1,...,x_k

但是以下Python函数

def gun(b_1,...,b_m):
   ...
   return y_1,...,y_l

由用户提供,此外,

fun_extra_args = (e_1,...,e_p)

如果使用Fortran或C函数调用回调参数,则应用以下规则gun

  • 如果那时被调用,这里 。p == 0gun(a_1, …, a_q)q = min(m, n)
  • 如果那时被召唤。n + p <= mgun(a_1, …, a_n, e_1, …, e_p)``
  • 如果那时被调用,这里 。p <= m < n + pgun(a_1, …, a_q, e_1, …, e_p)q=m-p
  • 如果那时被召唤。p > mgun(e_1, …, e_m)``
  • 如果小于所需参数的数量, 则引发异常。n + pgun``

该函数gun可以将任意数量的对象作为元组返回。然后应用以下规则:

  • 如果,则忽略。k < ly_{k + 1}, …, y_l``
  • 如果,那么只设置。k > lx_1, …, x_l``

常用块

F2PY common为例程签名块中定义的块生成包装器。所有与当前扩展模块链接的Fortran代码都可以看到公共块,但不能看到其他扩展模块(这种限制是由于Python导入共享库的原因)。在Python中,common块的F2PY包装器fortran是具有与公共块的数据成员相关的(动态)属性的类型对象。访问时,这些属性作为NumPy数组对象(多维数组是Fortran连续)返回,这些对象直接链接到公共块中的数据成员。可以通过直接赋值或通过对相应数组对象的就地更改来更改数据成员。

考虑以下Fortran 77代码:

C FILE: COMMON.F
      SUBROUTINE FOO
      INTEGER I,X
      REAL A
      COMMON /DATA/ I,X(4),A(2,3)
      PRINT*, "I=",I
      PRINT*, "X=[",X,"]"
      PRINT*, "A=["
      PRINT*, "[",A(1,1),",",A(1,2),",",A(1,3),"]"
      PRINT*, "[",A(2,1),",",A(2,2),",",A(2,3),"]"
      PRINT*, "]"
      END
C END OF COMMON.F

并使用它包装。f2py -c -m common common.f

在Python中:

>>> import common
>>> print common.data.__doc__
i - 'i'-scalar
x - 'i'-array(4)
a - 'f'-array(2,3)

>>> common.data.i = 5
>>> common.data.x[1] = 2 
>>> common.data.a = [[1,2,3],[4,5,6]]
>>> common.foo()
 I= 5
 X=[ 0 2 0 0]
 A=[
 [  1.,  2.,  3.]
 [  4.,  5.,  6.]
 ]
>>> common.data.a[1] = 45
>>> common.foo()
 I= 5
 X=[ 0 2 0 0]
 A=[
 [  1.,  2.,  3.]
 [  45.,  45.,  45.]
 ]
>>> common.data.a                 # a is Fortran-contiguous
array([[  1.,   2.,   3.],
       [ 45.,  45.,  45.]],'f')

Fortran 90模块数据

Fortran 90模块数据的F2PY接口类似于Fortran 77公共块。

考虑以下Fortran 90代码:

module mod
  integer i
  integer :: x(4)
  real, dimension(2,3) :: a
  real, allocatable, dimension(:,:) :: b 
contains
  subroutine foo
    integer k
    print*, "i=",i
    print*, "x=[",x,"]"
    print*, "a=["
    print*, "[",a(1,1),",",a(1,2),",",a(1,3),"]"
    print*, "[",a(2,1),",",a(2,2),",",a(2,3),"]"
    print*, "]"
    print*, "Setting a(1,2)=a(1,2)+3"
    a(1,2) = a(1,2)+3
  end subroutine foo
end module mod

并使用它包装。f2py -c -m moddata moddata.f90

在Python中:

>>> import moddata
>>> print moddata.mod.__doc__
i - 'i'-scalar
x - 'i'-array(4)
a - 'f'-array(2,3)
foo - Function signature:
  foo()


>>> moddata.mod.i = 5  
>>> moddata.mod.x[:2] = [1,2]
>>> moddata.mod.a = [[1,2,3],[4,5,6]]
>>> moddata.mod.foo()                
 i=           5
 x=[           1           2           0           0 ]
 a=[
 [   1.000000     ,   2.000000     ,   3.000000     ]
 [   4.000000     ,   5.000000     ,   6.000000     ]
 ]
 Setting a(1,2)=a(1,2)+3
>>> moddata.mod.a               # a is Fortran-contiguous
array([[ 1.,  5.,  3.],
       [ 4.,  5.,  6.]],'f')

可分配数组

F2PY对Fortran 90模块可分配阵列提供基本支持。

考虑以下Fortran 90代码:

module mod
  real, allocatable, dimension(:,:) :: b 
contains
  subroutine foo
    integer k
    if (allocated(b)) then
       print*, "b=["
       do k = 1,size(b,1)
          print*, b(k,1:size(b,2))
       enddo
       print*, "]"
    else
       print*, "b is not allocated"
    endif
  end subroutine foo
end module mod

并使用它包装。f2py -c -m allocarr allocarr.f90

在Python中:

>>> import allocarr 
>>> print allocarr.mod.__doc__
b - 'f'-array(-1,-1), not allocated
foo - Function signature:
  foo()

>>> allocarr.mod.foo()  
 b is not allocated
>>> allocarr.mod.b = [[1,2,3],[4,5,6]]         # allocate/initialize b
>>> allocarr.mod.foo()
 b=[
   1.000000       2.000000       3.000000    
   4.000000       5.000000       6.000000    
 ]
>>> allocarr.mod.b                             # b is Fortran-contiguous
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]],'f')
>>> allocarr.mod.b = [[1,2,3],[4,5,6],[7,8,9]] # reallocate/initialize b
>>> allocarr.mod.foo()
 b=[
   1.000000       2.000000       3.000000    
   4.000000       5.000000       6.000000    
   7.000000       8.000000       9.000000    
 ]
>>> allocarr.mod.b = None                      # deallocate array
>>> allocarr.mod.foo()
 b is not allocated

使用 F2PY

F2PY既可以用作命令行工具 f2py,也可以用作Python模块 numpy.f2py。 虽然我们尝试将命令行工具安装为numpy设置的一部分,但是像Windows这样的某些平台却难以将可执行文件可靠地放在 PATH 上。 我们将在本文档中引用 f2py,但您可能必须将其作为模块运行:

python -m numpy.f2py

如果你运行没有参数的 f2py,并且最后的行 numpy Version 与从 python -m numpy.f2py 打印的NumPy版本匹配, 那么你可以使用更短的版本。如果没有,或者如果你不能运行 f2py,你应该用更长的版本替换所有对 f2py 的调用。

f2py 命令

当用作命令行工具时,f2py 有三种主要模式,区别在于使用 -c-h 开关:要扫描Fortran源并生成签名文件,请使用:

  1. 要扫描Fortran源并生成签名文件,请使用

    f2py -h <filename.pyf> <options> <fortran files>   \
      [[ only: <fortran functions>  : ]                \
      [ skip: <fortran functions>  : ]]...            \
      [<fortran files> ...]

    请注意,Fortran源文件可以包含许多例程,并且不一定需要从Python中使用所有例程。 因此,您可以指定应该包装哪些例程( 在 only: .. : 部分)或者应忽略哪些例程F2PY(在 skip: .. : 部分)。

    如果将 <filename.pyf> 指定为 stdout,则签名将发送到标准输出而不是文件。

    在其他选项(见下文)中,可以在此模式中使用以下选项:

    --overwrite-signature

    覆盖现有签名文件。

  2. 要构建扩展模块,请使用:

    f2py <options> <fortran files>          \
      [[ only: <fortran functions>  : ]     \
      [ skip: <fortran functions>  : ]]... \
      [<fortran files> ...]

    构造的扩展模块作为 <modulename>module.c 保存到当前目录。

    这里 <fortran files> 也可能包含签名文件。在其他选项(见下文)中,可以在此模式中使用以下选项:

    • --debug-capi

      将调试挂钩添加到扩展模块。使用此扩展模块时,有关包装器的各种信息将打印到标准输出,例如,变量值,所采取的步骤等。

    • -include'<includefile>'

      将CPP #include 语句添加到扩展模块源。 <includefile> 应以下列形式之一给出:

      "filename.ext"
      

      include语句就在包装函数之前插入。此功能允许在F2PY生成的包装器中使用任意C函数(在 <includefile> 中定义)。

      不推荐使用此选项。使用usercode语句直接在签名文件中指定C代码片段。

    • --[no-]wrap-functions

      为 Fortran 函数创建 Fortran子例程包装器。--wrap-functions 是默认的,因为它确保了最大的可移植性和编译器独立性。

    • --include-paths <path1>:<path2>:..

      搜索包含给定目录中的文件。

    • --help-link [<list of resources names>] 列出 numpy_distutils/system_info.py 找到的系统资源。 例如,尝试 f2py --help-link lapack_opt

  3. 要构建扩展模块,请使用

    f2py -c <options> <fortran files>       \
      [[ only: <fortran functions>  : ]     \
      [ skip: <fortran functions>  : ]]... \
      [ <fortran/c source files> ] [ <.o, .a, .so files> ]

    如果 <fortran files> 包含签名文件,则构建扩展模块的源,编译所有Fortran和C源,最后将所有对象和库文件链接到扩展模块 <modulename>.so,保存到 当前目录。

    如果 <fortran files> 不包含签名文件,则通过扫描所有Fortran源代码以构建常规签名来构建扩展模块。

    在以前模式中描述的其他选项(参见下文)和选项中,可以在此模式中使用以下选项:

    • --help-fcompiler

      列出可用的Fortran编译器。

    • --help-compiler [depreciated]

      列出可用的Fortran编译器。

    • --fcompiler=<Vendor>

      按供应商指定Fortran编译器类型。

    • --f77exec=<path>

      指定F77编译器的路径。

    • --fcompiler-exec=<path> [depreciated]

      指定F77编译器的路径。

    • --f90exec=<path>

      指定F90编译器的路径。

    • --f90compiler-exec=<path> [depreciated]

      指定F90编译器的路径。

    • --f77flags=<string>

      指定F77编译器标志。

    • --f90flags=<string>

      指定F90编译器标志。

    • --opt=<string>

      指定优化标志。

    • --arch=<string>

      指定架构特定的优化标志。

    • --noopt

      无需优化即可编译。

    • --noarch

      编译时不依赖于arch的优化。

    • --debug

      编译调试信息。

    • -l<libname>

      链接时使用库<libname>

    • -D<macro>[=<defn=1>]

      将宏 <macro> 定义为 <defn>

    • -U<macro>

      定义宏<macro>

    • -I<dir>

      将目录 <dir> 添加到搜索包含文件的目录列表中。

    • -L<dir>

      将目录 <dir> 添加到要搜索 -l 的目录列表中。

    • link-<resource>

      使用 numpy_distutils/system_info.py 定义的 <resource> 链接扩展模块。例如。要链接优化的LAPACK库(MacOSX上的vecLib,其他地方的ATLAS),请使用 –link-lapack_opt。 另请参阅 –help-link 开关。

    构建扩展模块时,非gcc Fortran编译器可能需要以下宏的组合:

    -DPREPEND_FORTRAN
    -DNO_APPEND_FORTRAN
    -DUPPERCASE_FORTRAN

    要测试 F2PY 生成的接口的性能,请使用 -DF2PY_REPORT_ATEXIT。 然后在Python的出口处打印出各种计时的报告。 此功能可能无法在所有平台上运行,目前仅支持Linux平台。

    要查看F2PY生成的接口是否执行数组参数的副本,请使用 -DF2PY_REPORT_ON_ARRAY_COPY=<int>。 当数组参数的大小大于 <int> 时,会将有关应对的消息发送到stderr。

其他选择:

  • -m <modulename>

    扩展模块的名称。默认为无标题。如果使用签名文件(*.pyf),请不要使用此选项。

  • --[no-]lower

    不要降低 <fortran files> 中的大小写。 默认情况下,--lower 假定为 -h 开关, --no-lower 假定为 -h 开关。

  • --build-dir <dirname>

    所有F2PY生成的文件都在 <dirname> 中创建。默认值为 tempfile.mkdtemp()

  • --quiet

    安静地跑(不打印日志)。

  • --verbose

    额外冗长的跑(打印大量日志)。

  • -v

    打印f2py版本ID并退出。

在没有任何选项的情况下执行 f2py 以获取可用选项的最新列表。

Python 模块 numpy.f2py

警告

f2py 模块的当前Python接口尚未成熟,将来可能会发生变化。

Fortran到Python接口生成器。

  • numpy.f2py.run_main( comline_list )[点击查看源代码]

    相当于运行:

    f2py <args>

    其中 =string.join(,' '),但在Python中。除非使用 -h,否则此函数将返回一个字典,其中包含有关生成的模块及其对源文件的依赖关系的信息。例如,可以从Python执行命令 f2py -m scalar scalar.f ,如下所示:

    您无法使用此功能构建扩展模块,即不允许使用 -c。请改用 compile 命令。

    示例:

    >>> import numpy.f2py
    >>> r = numpy.f2py.run_main(['-m','scalar','doc/source/f2py/scalar.f'])
    Reading fortran codes...
            Reading file 'doc/source/f2py/scalar.f' (format:fix,strict)
    Post-processing...
            Block: scalar
                            Block: FOO
    Building modules...
            Building module "scalar"...
            Wrote C/API module "scalar" to file "./scalarmodule.c"
    >>> print(r)
    {'scalar': {'h': ['/home/users/pearu/src_cvs/f2py/src/fortranobject.h'],
            'csrc': ['./scalarmodule.c', 
                      '/home/users/pearu/src_cvs/f2py/src/fortranobject.c']}}
  • numpy.f2py.compile( source , modulename=’untitled’ , extra_args=’’ , verbose=True , source_fn=None , extension=’.f’ )[点击查看源代码]

    使用f2py从Fortran 77源字符串构建扩展模块。

    参数:

    类型 描述
    source : str or bytes 要编译的Fortran源模块/子程序。在版本1.16.0中更改: 接受str以及字节。
    modulename : str, optional 已编译的python模块的名称
    extra_args : str or list, optional 传递给f2py的其他参数。版本1.16.0中已更改: 也可能提供args列表。
    verbose : bool, optional 将f2py输出打印到屏幕
    source_fn : str, optional 写入fortran源的文件的名称。 默认设置是使用扩展参数提供的扩展名的临时文件。
    extension : {‘.f’, ‘.f90’}, optional 如果未提供source_fn,则为文件扩展名。扩展名告诉我使用了哪个fortran标准。默认值为f,表示F77标准。 版本1.11.0中的新功能。

    返回值:

    类型 描述
    result : int 0 表示成功

    示例:

    >>> import numpy.f2py
    >>> fsource = '''
    ...       subroutine foo
    ...       print*, "Hello world!"
    ...       end 
    ... '''
    >>> numpy.f2py.compile(fsource, modulename='hello', verbose=0)
    0
    >>> import hello
    >>> hello.foo()
    Hello world!

使用 numpy.distutils 模块

numpy.distutils 是NumPy扩展标准Python distutils的一部分, 用于处理Fortran源代码和F2PY签名文件, 例如编译Fortran源代码,调用F2PY构造扩展模块等。

示例

请思考下面的setup 文件

from __future__ import division, absolute_import, print_function

from numpy.distutils.core import Extension

ext1 = Extension(name = 'scalar',
                 sources = ['scalar.f'])
ext2 = Extension(name = 'fib2',
                 sources = ['fib2.pyf', 'fib1.f'])

if __name__ == "__main__":
    from numpy.distutils.core import setup
    setup(name = 'f2py_example',
          description       = "F2PY Users Guide examples",
          author            = "Pearu Peterson",
          author_email      = "pearu@cens.ioc.ee",
          ext_modules = [ext1, ext2]
          )
# End of setup_example.py

运行

python setup_example.py build

将构建两个扩展模块 标量fib2 到构建目录。

numpy.distutils 使用以下功能扩展 distutils

  • 扩展 类参数 可能包含Fortran源文件。此外,列表 最多可包含一个F2PY签名文件,然后扩展模块的名称必须与签名文件中使用的<modulename> 匹配。 假设F2PY签名文件恰好包含一个 python模块 块。

    如果 文件不包含签名文件,则使用 F2PY 扫描Fortran 源文件中的例程签名,以构造 Fortran 代码的包装器。

    可以使用 扩展 类参数 f2py_options 给出 F2py 进程的其他选项。

  • 定义了以下新的 distutils 命令:

    build_src

    构建Fortran包装器扩展模块,以及其他许多事情。

    config_fc

    更改Fortran编译器选项

    以及 build_extbuild_clib 命令都得到了增强,以支持Fortran源代码。

    运行

    python <setup.py file> config_fc build_src build_ext --help

    要查看这些命令的可用选项,请执行以下操作。

  • 在构建包含 Fortran 源代码的 Python 包时,可以使用 build_ext 命令选项 --fcompiler=<Vendor>. 来选择不同的Fortran编译器。此处<Vendor> 可以是以下名称之一:

    absoft sun mips intel intelv intele intelev nag compaq compaqv gnu vast pg hpux

    有关支持的编译器或运行的最新列表,请参见 numpy_distutils/fCompiler.py

    f2py -c --help-fcompiler

高级F2PY用法

将自编写函数添加到F2PY生成的模块

可以使用 usercodepymethoddef 语句在签名文件中定义自编的Python C / API函数(它们必须在 python模块 块中使用)。 例如,以下签名文件spam.pyf

!    -*- f90 -*-
python module spam
    usercode '''
  static char doc_spam_system[] = "Execute a shell command.";
  static PyObject *spam_system(PyObject *self, PyObject *args)
  {
    char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return Py_BuildValue("i", sts);
  }
    '''
    pymethoddef '''
    {"system",  spam_system, METH_VARARGS, doc_spam_system},
    '''
end python module spam

包装C库函数system()

f2py -c spam.pyf

在Python中:

>>> import spam
>>> status = spam.system('whoami')
pearu
>> status = spam.system('blah')
sh: line 1: blah: command not found

修改F2PY生成模块的字典

以下示例说明如何将用户定义的变量添加到F2PY生成的扩展模块。给出以下签名文件:

!    -*- f90 -*-
python module var
  usercode '''
    int BAR = 5;
  '''
  interface
    usercode '''
      PyDict_SetItemString(d,"BAR",PyInt_FromLong(BAR));
    '''
  end interface
end python module

将其编译为:f2py -c var.pyf

请注意,必须在 interface(接口) 块内定义第二个 usercode 语句,并且通过变量 d 可以获得模块字典(有关其他详细信息,请参阅f2py var.pyf 生成的 varmodule.c)。

在Python中:

>>> import var
>>> var.BAR
5

文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录