使用 f2py 公开子例程的 "in,out" 和 "inplace" 版本

Expose both "in,out" and "inplace" versions of subroutine with f2py

我正在使用 f2py 将一些 Fortran77 例程集成到我的 python 模块中,但无法找到解决以下问题的好方法。我的 Fortran 例程在计算过程中销毁所有输入数组。我想为用户提供使用这些例程的内存高效 "inplace" 行为的选项,以及在不破坏其 Python 边数据的情况下执行例程的选项。

我可以想到两种方法:

  1. 如果用户不想破坏输入,请在调用我的 Fortran 例程之前备份 Python 中的所有输入数组。在我的签名中使用 "intent(inplace)" 声明编译 Fortran 例程。退出时,我可以使用备份副本恢复输入。
  2. 针对两个不同的签名文件编译相同的 Fortran 例程,一个带有 "intent(inplace)",一个带有 "intent(in,out)"(或仅 "intent(in)",视情况而定)。然后在 python 中,我可以将两种风格的例程导入到我的模块中。

我不太喜欢这两种方法,因为它们看起来都不pythonic。有没有更好的方法来解决这个问题?是否有更简单的方法来实现方法 2,例如我可以将具有两个不同签名的相同例程编译到一个共享对象模块中吗?

这是一个示例(基于此 documentation)。

Fortran 例程fib.f:

C FILE: FIB.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 FIB.F

我使用 f2py fib.f -m fib.pyf 生成我的签名文件,然后添加一些意图声明:

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module fib ! in
    interface  ! in :fib
        subroutine fib(a,n) ! in :fib:fib.f
            real*8 dimension(n), intent(inout) :: a
            integer, optional,check(len(a)>=n),depend(a) :: n=len(a)
        end subroutine fib
    end interface
end python module fib

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

我用 f2py -c fib.pyf fib.f 编译,然后我可以 运行 我的例程到位:

>>> import numpy as np
>>> from fib import fib
>>> A = np.zeros(10, dtype=float)
>>> fib(A)
>>> print(A)
[  0.   1.   1.   2.   3.   5.   8.  13.  21.  34.]

要将此示例更改为 in/out 模式,我只需将 "fib.pyf" 中的意图声明更改为 intent(in,out)

我最终使用签名文件中的 fortranname 声明来生成同一子例程的就地和输入、输出风格。这是签名文件:

    !    -*- f90 -*-
    ! Note: the context of this file is case sensitive.

    python module fib ! in
            interface  ! in :fib
                subroutine fib_inplace(a,n) ! in :fib:fib.f
                    fortranname fib
                    real*8 dimension(n), intent(inout) :: a
                    integer, optional,check(len(a)>=n),depend(a) :: n=len(a)
                end subroutine fib
                subroutine fib(a,n) ! in :fib:fib.f
                    fortranname fib
                    real*8 dimension(n), intent(copy, in,out) :: a
                    integer, optional,check(len(a)>=n),depend(a) :: n=len(a)
                end subroutine fib
            end interface
    end python module fib

注意:为了真正防止第二个例程修改 Python 输入,我还必须将 copy 添加到 intent 指令中。