在遇到有关f2py和回调函数的examples shown in the numpy docs之一时,我遇到了麻烦。
我所执行的确切相同的步骤,在第一个例子(即,f2py -c -m callback callback.f
)来包装callback.f
:
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
但是,如示例中那样测试结果,可以得到:
python
>>> import callback
>>> def f(i): return i*i
...
>>> print callback.foo(f)
0.0
因此,它返回0.0
而不是110.0
,其中0.0
是Fortran代码中r
的初始值。
无论我使用哪个回调函数,结果都将保持不变(不变的R
)。
我正在使用从python 3.7
获得的numpy
和conda
的最新版本。
你能重现这个问题,还是我做错了什么?
答案 0 :(得分:2)
问题似乎是由外部函数FUN
的预期和实际数据类型之间的不匹配引起的:
CALLBACK.F
中,EXTERNAL
函数FUN
具有隐式类型REAL
(因为没有显式类型,或者{ {1}}语句)。 IMPLICIT NONE
显式创建了F2PY包装器,您会注意到外部函数{{1 }}被定义为具有f2py -m callback2 -h callback2.pyf callback.f
类型(未修改的r
签名文件也是如此,因此这是默认的F2PY行为)。简而言之,问题在于fun
期望real*8 :: r
返回callback2.pyf
的结果,而从Python和F2PY包装器的那一侧定义了外部函数以返回{{1} }结果。因此,一种解决方案是确保FOO
在Fortran和Python / F2PY包装器中具有相同的返回类型。这可以通过多种方式来实现,例如,通过在FUN
中添加数据类型规范REAL
:
REAL*8
如示例中用FUN
包裹经过修改的REAL*8 FUN
,现在将提供所需的输出:
CALLBACK.F
出于兴趣的考虑,可以使用两个文件C FILE: CALLBACK.F
SUBROUTINE FOO(FUN,R)
REAL*8 FUN
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
和CALLBACK.F
在纯Fortran中产生与Python / F2PY中相同的行为:
python -m numpy.f2py -c -m callback callback.f
和
python
>>> import callback
>>> def f(i): return i*i
...
>>> print(callback.foo(f))
110.0
使用prog.f
进行编译,它提供以下输出(实际上与您遇到问题的Python示例相同):
fun.f
取消注释C FILE: PROG.F, including subroutine FOO previously in CALLBACK.F
PROGRAM MAIN
C REAL*8 FUN
EXTERNAL FUN
REAL*8 R
R = 0
PRINT *, "BEFORE: ", R
CALL FOO(FUN, R)
PRINT *, "AFTER: ", R
END
C This is copied from CALLBACK.F
SUBROUTINE FOO(FUN,R)
C REAL*8 FUN
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
中C FILE: FUN.F containing the function to be called by FOO
REAL*8 FUNCTION FUN(I)
INTEGER I
FUN = I*I
END
的两个实例并重新编译可以解决问题:
gfortran -fcheck=all -Wall -g func.f prog.f