[我在https://numpy.org/devdocs/f2py/python-usage.html#call-back-arguments中找到了一个有启发性的例子。这里是fortran例程:
C FILE: CALLBACK.F
SUBROUTINE FOO(FUN,R)
EXTERNAL FUN
INTEGER I
REAL*8 R, FUN
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__)
def f(i):
return i * i
print(callback.foo(f))
一切正常。现在,我想使用ctypes重复测试。我可以使用以下命令轻松编译fortran源:gfortran -shared callback.f -o callback.dll我可以用以下方式加载库:
import ctypes as ct
import numpy as np
# import the dll
fortlib = ct.CDLL('callback.dll')
问题:
谢谢。 Gianmarco
平台:Anaconda python 3.7.6,在Windows 10上为Mingw-64
良好的编程风格指示我们从不使用单字符变量名称。经验丰富的现代Fortran程序员将实现类似于以下内容的Fortran函数:
module foo_mod
use iso_c_binding, only: RK => c_double, IK => c_int32_t
implicit none
abstract interface
function getFunVal_proc(inputInteger) result(funVal) bind(C)
import :: RK, IK
implicit none
integer(IK), intent(in), value :: inputInteger
real(RK) :: funVal
end function getFunVal_proc
end interface
contains
subroutine getFoo(getFunValFromC,outputReal) bind(C,name="getFoo")
!DEC$ ATTRIBUTES DLLEXPORT :: getFoo
use, intrinsic :: iso_c_binding, only: c_funptr, c_f_procpointer
implicit none
type(c_funptr), intent(in), value :: getFunValFromC
procedure(getFunVal_proc), pointer :: getFunVal
real(RK), intent(out) :: outputReal
integer(IK) :: indx
! associate the input C procedure pointer to a Fortran procedure pointer
call c_f_procpointer(cptr=getFunValFromC, fptr=getFunVal)
outputReal = 0._RK
do indx = -5,5
write(*,"(*(g0,:,' '))") "value of indx from inside Fortran: ", indx
outputReal = outputReal + getFunVal(indx)
end do
write(*,"(*(g0,:,' '))") "value of outputReal from inside Fortran: ", outputReal
! nullify the Fortran pointer
nullify(getFunVal)
end subroutine getFoo
end module foo_mod
这看起来很冗长,但是比F77好得多。毕竟,我们生活在21世纪。然后,您可以通过Intel ifort编译此Fortran代码,例如,
ifort /dll /threads /libs:static foo_mod.f90 /exe:foo.dll
然后,您将像下面的Python脚本一样从生成的DLL getFoo()
中调用foo.dll
,>
import ctypes as ct import numpy as np def getSquare(inputInteger): print("value of indx received by getSquare() inside Python: ",inputInteger) return np.double(inputInteger**2) # define ctypes wrapper function, with the proper result and argument types getFunVal_proc = ct.CFUNCTYPE( ct.c_double # callback (python) function result , ct.c_int32 # callback (python) function input integer argument ) getSquare_pntr = getFunVal_proc(getSquare) libpath = "foo.dll" try: foolib = ct.CDLL(libpath) except Exception as e: import logging logger = logging.Logger("catch_all") logger.error(e, exc_info=True) # define getFoo's interface from Fortran dll foolib.getFoo.restype = None foolib.getFoo.argtypes = [ getFunVal_proc # procedure , ct.POINTER(ct.c_double) # real64 return value , ] outputReal = ct.c_double(0.) foolib.getFoo ( getSquare_pntr , ct.byref(outputReal) ) print("value of outputReal received in Python: ", np.double(outputReal))
运行此脚本将产生类似以下的内容,
In [1]: run main.py value of indx from inside Fortran: -5 value of indx received by getSquare() inside Python: -5 value of indx from inside Fortran: -4 value of indx received by getSquare() inside Python: -4 value of indx from inside Fortran: -3 value of indx received by getSquare() inside Python: -3 value of indx from inside Fortran: -2 value of indx received by getSquare() inside Python: -2 value of indx from inside Fortran: -1 value of indx received by getSquare() inside Python: -1 value of indx from inside Fortran: 0 value of indx received by getSquare() inside Python: 0 value of indx from inside Fortran: 1 value of indx received by getSquare() inside Python: 1 value of indx from inside Fortran: 2 value of indx received by getSquare() inside Python: 2 value of indx from inside Fortran: 3 value of indx received by getSquare() inside Python: 3 value of indx from inside Fortran: 4 value of indx received by getSquare() inside Python: 4 value of indx from inside Fortran: 5 value of indx received by getSquare() inside Python: 5 value of outputReal from inside Fortran: 110.0000000000000 value of outputReal received in Python: 110.0
与您的F2PY代码相比,该Python脚本可能看起来又很冗长。但是,与Python和Fortran标准相比,它比您的实现更加专业,现代且符合标准。
脚注:在Windows,Linux和Mac平台上,所有学生,教师和开源开发人员均可免费获得Intel ifort。这并不意味着gfortran不好。但是我认为,在Windows操作系统上使用gcc通常并不比永无止境的噩梦更好。