用ctypes调用Python中的Fortran共享库
我试图在Python中使用Fortran模块。我在Python中有几个数组——数值数组和字符串数组。我用Fortran和Python定义了数组,但我认为我对数值数组使用了错误的类型。我得到一个错误,转换第一个参数(数值数组)失败。我应该使用什么类型 错误:用ctypes调用Python中的Fortran共享库,python,fortran,ctypes,Python,Fortran,Ctypes,我试图在Python中使用Fortran模块。我在Python中有几个数组——数值数组和字符串数组。我用Fortran和Python定义了数组,但我认为我对数值数组使用了错误的类型。我得到一个错误,转换第一个参数(数值数组)失败。我应该使用什么类型 错误: Traceback (most recent call last): File "py_try.py", line 66, in <module> writelittler.write_obs(p,z,t,td,spd
Traceback (most recent call last):
File "py_try.py", line 66, in <module>
writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat,xlon,date_char,num_met,num_lev,kx,dd_strvar,station_strvar,synop,string4, bogus, iseq_num, iunit)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1
Python:
Traceback (most recent call last):
File "py_try.py", line 66, in <module>
writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat,xlon,date_char,num_met,num_lev,kx,dd_strvar,station_strvar,synop,string4, bogus, iseq_num, iunit)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1
将numpy导入为np
导入ctypes
从ctypes导入c_int、c_char
writelittler=ctypes.CDLL(“/writelittler.so”)
p=np.asarray([982.6999.7],dtype=“float64”)
p\u strvar=ctypes.c\u void\u p(p.ctypes.data)
#[省略其他数字数组]
虚假=0
kx=2
iseq_num=0
iunit=2
日期字符='20160128060000'
dt=np.dtype('a40')
dd=np.asarray(['11111111111111111111111111111111111111111111111','66666666666666666666666666','99999999999999999999999',dtype=dt)
station=np.asarray(['V111111111111111111','M111111111111111111111',dtype=dt)
dd_strvar=ctypes.c_void_p(dd.ctypes.data)
station\u strvar=ctypes.c\u void\u p(station.ctypes.data)
num_met=6
num_lev=1
synop='FM-12 synop'
字符串4=''
writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat,xlon,date_char,num_met,num_lev,kx,dd_strvar,station_strvar,synop,string4,false,iseq_num,iunit)
您没有在Python中指定Fortran子例程的参数类型。此外,您也不会声明/初始化传递给Fortran的变量。因此,Python不知道如何处理它。这就是它在错误消息中告诉您的
由于Fortran默认使用按引用调用,因此argtypes
为:
writelittler.write_obs.argtypes = [ POINTER(c_float), # p
POINTER(c_float), # z
POINTER(c_float), # t
POINTER(c_float), # td
POINTER(c_float), # spd
POINTER(c_float), # wdir
POINTER(c_int), # kx
POINTER(c_float), # slp
POINTER(c_float), # ter
POINTER(c_float), # xlat
POINTER(c_char), # date_char
POINTER(c_char), # dd
POINTER(c_char), # station
POINTER(c_int), # num_met
POINTER(c_int), # num_lev
POINTER(c_char), # synop
POINTER(c_int), # string4
POINTER(c_bool), # bogus
POINTER(c_bool), # string4
POINTER(c_int), # iseq_num
POINTER(c_int) # iunit
]
[这可能可以简化…]
此外,Fortran子例程没有返回值:
writelittler.write_obs.restype = None
此外,还需要调整函数调用本身(在Python中):
[这未经验证…]
既然Python知道会发生什么,您仍然需要声明缺少的变量。正如您的代码所知,它将导致以下错误:
回溯(最近一次呼叫最后一次):
文件“test.py”,第51行,在
写入者。写入对象(p、z、t、td、spd、wdir、slp、ter、xlat、,
xlon,date\u char,num\u met,num\u lev,kx,dd\u strvar,
电台(标准电台、会议、string4、伪造、iseq_num、iunit)
NameError:未定义名称“z”
传递numpy数组时,请使用其
ctypes
属性,例如x.ctypes
而不是c\u void\p(x.ctypes.data)
。对于第一个参数,您错误地传递了numpy数组p
,而不是p.ctypes
。确保数字类型匹配(例如c_float
float32
,c_double
float64
)。对于按值传递,Fortran的c绑定不要求您显式声明参数为byvalue
?否则,参数将通过引用传递。例如,integer(c_int)、intent(in)、value::k、kx
@eryksun。但是我建议将argtypes显式声明为指针(c_float)
,等等@Palina在Python代码中指定z
、t
,以及所有其他变量的位置?您正在用Fortran访问它们!如果我要声明argtypes
,那么我会使用numpy指针类型,比如npf8arr\u t=np.ctypeslib.ndpointer(dtype='float64')
。这样,OP可以直接传递numpy数组x
,而不是传递x.ctypes
。此外,如果OP控制Fortran API,则应将整数参数更改为通过值传递。否则这将是一个乏味的接口,例如num_met=ctypes.c_int(6)
<代码>写入对象(…,ctypes.byref(num_met),…)
。而不是简单地num_met=6
write\u obs(…,num\u met,…)
@eryksun实际上,我更喜欢更乏味的写作方式。通常,会传递大的numpy数组,我会尽量避免复制内容。但是你是对的,我也应该将更改添加到函数调用中。谁说要复制数组参数?ndpointer
类型不复制参数。它的from_param
方法验证numpy数组类型是否与类型、维度、形状和标志中所需的约束匹配,然后它只传递ctypes
属性,该属性将\u定义为参数\u
属性,该属性将数组的基地址作为c\u void\p
实例。没有创建副本。@eryksun在Fortran中,如果指定value
则切换为按值调用。
Traceback (most recent call last):
File "test.py", line 51, in <module>
writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat,
xlon,date_char,num_met,num_lev,kx,dd_strvar,
station_strvar,synop,string4, bogus, iseq_num, iunit)
NameError: name 'z' is not defined