在Python中从共享的Fortran库调用函数

16

我想在Python中调用来自Fortran共享库的某些函数。我在网上找到了一些链接并阅读了它们,根据我所发现的,我应该这样做:

libadd = cdll.LoadLibrary('./libbin.so') 

需要加载共享对象。然而,这个共享对象包含另一个共享库中的一些符号。我读了cdll的帮助文档,但似乎不可能同时加载多个共享对象文件。我该如何调用这个Fortran库中的函数,这个库很可能是由Intel Fortran编译器编译的?


您可能还想看看f2py。它是NumPy的一部分。 - lafras
3个回答

22
您需要知道共享对象中函数的签名。您是否有源代码或某些参考资料,可以解释函数名称和参数类型?
例如,我有这个源代码(mult.f90):
integer function multiply(a, b)
    integer, intent(in) :: a, b
    multiply = a * b
end function multiply

为了演示如何同时加载和使用多个共享对象,我还有一个 (add.f90) 文件:

integer function addtwo(a, b)
    integer, intent(in) :: a, b
    addtwo = a + b
end function addtwo

编译,检查符号:

% gfortran-4.4 -shared -fPIC -g -o mult.so mult.f90
% gfortran-4.4 -shared -fPIC -g -o add.so add.f90
% nm -ao mult.so | grep multiply
mult.so:00000000000005cc T multiply_

注意共享对象中的符号名称后面有一个下划线。由于我有源代码,所以我知道签名是multiply_(int *a,int *b),因此可以轻松地从 ctypes 调用该函数:

from ctypes import byref, cdll, c_int

mult = cdll.LoadLibrary('./mult.so')
add = cdll.LoadLibrary('./add.so')
a = c_int(2)
b = c_int(4)
print mult.multiply_(byref(a), byref(b))
print add.addtwo_(byref(a), byref(b))

输出:

8
6

感谢您提供的精彩答案。我有一个快速的跟进问题。当我检查库的名称列表(nm mult.so)时,它显示为_multiply_而不是multiply_,尽管后者在C和Python中仍然有效。您知道这是为什么吗? - marshall.ward
不客气。不同的编译器可能会在符号名称前缀/后缀下划线。我知道在gfortran的情况下,您可以提供“-fno-underscoring”来关闭它;从而得到该函数的符号“multiply”。您的编译器+平台可能具有此选项。如果您正在处理来源未知的共享对象,则我将使用另一种可能的解决方案更新答案。 - samplebias
实际上,我会等到听到编译器选项的反馈后再进行进一步评论,因为那可能会为您解决问题。重要的是要知道您调用的函数的签名,所以您希望通过在上游纠正事物来最小化猜测。 - samplebias
@samplebias 我应该提到我正在使用的是OS X操作系统。在Linux库中,前导下划线不会出现,所以这可能是OS X(或BSD)的特性。由于这超出了问题的范围,我应该就此结束。感谢您的关注。 - marshall.ward
@samplebias:有一些符号在其中一个共享对象文件中被引用,但仍未定义。而这些符号位于另一个对象文件中,然而尝试加载该共享对象时会出现错误,提示无法识别该对象文件,你有任何想法吗? - Umut Tabak
@Umut 嗯,听起来可能没有正确地编译为您的平台,例如没有使用“-shared”和“-fPIC”,可能是您平台的CPU架构错误等。很难从这里判断。您可能会在未来遇到另一个问题:当您从本地目录加载共享对象,并且该.so链接到其他共享对象时,您需要设置“LD_LIBRARY_PATH”,以便加载器可以找到所有所需的库并将它们链接到可执行文件中。 - samplebias

5

我想补充一下@sameplebias的答案,可以使用iso_c_binding模块来强制(任何)Fortran编译器生成正确的C签名。以下是用法示例:

module fmesh_wrapper

use iso_c_binding, only: c_double, c_int
use fmesh, only: mesh_exp

implicit none

contains

subroutine c_mesh_exp(r_min, r_max, a, N, mesh) bind(c)
real(c_double), intent(in) :: r_min
real(c_double), intent(in) :: r_max
real(c_double), intent(in) :: a
integer(c_int), intent(in) :: N
real(c_double), intent(out) :: mesh(N)
call mesh_exp(r_min, r_max, a, N, mesh)
end subroutine

! wrap more functions here
! ...

end module

这将有以下的C函数签名:

void c_mesh_exp(double *r_min, double *r_max, double *a, int *N,
        double *mesh);

然后您可以像通常调用Python一样调用它。这种方法的优点是它适用于所有平台(无需使用任何特殊的编译器选项)。


1
要使来自NumPy的f2py正常工作,需要从@samplebias中借用mult.f90add.f90示例。从shell中编译Python可导入共享库:
f2py -c -m mult mult.f90
f2py -c -m add add.f90

现在在Python中使用它们:
>>> import add
>>> import mult
>>> add.addtwo(4, 5)
9
>>> mult.multiply(4, 5)
20

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接