ctypes.cast 在 python2 中工作并在 python3 中抛出 ArgumentError
Posted
技术标签:
【中文标题】ctypes.cast 在 python2 中工作并在 python3 中抛出 ArgumentError【英文标题】:ctypes.cast works in python2 and throws ArgumentError in python3 【发布时间】:2015-09-25 05:36:10 【问题描述】:我遇到了一个问题,我的 ctypes 代码在 python2 中工作,但在 python3 中失败。
我失败的函数是 arrptr_to_np,它试图获取在外部 c 库中创建的数组并将其加载到 numpy 数组中。
函数长这样
def arrptr_to_np(c_arrptr, shape, arr_t, dtype):
"""
Casts an array pointer from C to numpy
Args:
c_arrpt (uint64): a pointer to an array returned from C
shape (tuple): shape of the underlying array being pointed to
arr_t (PyCSimpleType): the ctypes datatype of c_arrptr
dtype (dtype): numpy datatype the array will be to cast into
"""
byte_t = ctypes.c_char
itemsize_ = dtype().itemsize
dtype_t = byte_t * itemsize_
dtype_ptr_t = C.POINTER(dtype_t) # size of each item
typed_c_arrptr = c_arrptr.astype(int)
c_arr = C.cast(typed_c_arrptr, dtype_ptr_t) # cast to ctypes
np_arr = np.ctypeslib.as_array(c_arr, shape)
np_arr.dtype = dtype
return np_arr
这些是我正在使用的示例中的变量值
varname - value - type(var)
c_arrptr - 20622304 - numpy.uint64
形状 - (506, 6) - 元组
arr_t - numpy.ctypeslib.ndpointer_
dtype - np.float32 - np.float32
这些在python2和python3版本之间是一样的(当然除了指针的值,但还是uint64)
当我执行该函数时,它在 python2 中按预期工作。 但是,在 python3 中,我在这一行出现错误
c_arr = C.cast(typed_c_arrptr, dtype_ptr_t) # cast to ctypes
错误是 ArgumentError
/usr/lib/python3.4/ctypes/__init__.py in cast(obj, typ)
487 def cast(obj, typ):
--> 488 return _cast(obj, obj, typ)
489
ArgumentError: argument 1: <class 'TypeError'>: wrong type
During handling of the above exception, another exception occurred:
ArgumentError Traceback (most recent call last)
in <module>()
----> 1 c_arr = C.cast(typed_c_arrptr, dtype_ptr_t) # cast to ctypes
/usr/lib/python3.4/ctypes/__init__.py in cast(obj, typ)
486 _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
487 def cast(obj, typ):
--> 488 return _cast(obj, obj, typ)
489
490 _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
ArgumentError: argument 1: <class 'TypeError'>: wrong type
在代码执行的那一点上,typed_c_arrptr 在程序的两个版本中都是一个 dtype('int64')。 dtype_ptr_t 是一个 LP_c_char_Array_4 也在两个版本中。
我尝试了许多关于 typed_c_arrptr = c_arrptr.astype(int) 的变体,将 int 替换为 ctypes.c_int、ctypes.c_long、ctypes.c_size_t。在这一点上,我只是在猜测要做什么以及出了什么问题。对此的任何帮助将不胜感激。
【问题讨论】:
如果在每个版本中将int
替换为long
会发生什么?
long 在 python3 中不作为关键字存在,在 python 2 中它不起作用。我也尝试过使用 ctypes.c_long。
我没有 Python 3,但据我所知,int
与 Python 2 的大小不同。long
的大小与 Python 2 中的 int
不同,并且这似乎也导致您的代码失败。可能是一个提示。
我知道这种差异,但这似乎不是问题。也许应该更改 ctypes.c_int32 或类似的东西,但即使发生这种情况,问题仍然存在。
【参考方案1】:
错误告诉你,第一个参数无法转换为ctypes.c_void_p
。那就是typed_c_arrptr
无法转换。
发生这种情况是因为astype()
在两个 Python 版本中的工作方式不同。使用 Python 3,您拥有
>>> isinstance(np.uint64(12345).astype(int), int)
False
因此 ctypes 不知道如何转换 np.uint64
。而在 Python 2 中,您拥有
>>> isinstance(np.uint64(12345).astype(int), int)
True
所以 ctypes 只是将其视为int
。
进一步the documentation of generic.astype()
阅读
未实现(虚拟属性)
类泛型的存在只是为了从 numpy 标量中派生出来,并拥有(尽管未实现)ndarray 类的所有属性,以提供统一的 API。
我不知道为什么它适用于 Python 2。
相反,您可以只使用int()
将np.uint64
转换为可以转换为ctypes.c_void_p
的东西。这对我的两个版本都有效。
c_arr = C.cast(int(c_arrptr), dtype_ptr_t)
【讨论】:
以上是关于ctypes.cast 在 python2 中工作并在 python3 中抛出 ArgumentError的主要内容,如果未能解决你的问题,请参考以下文章
读取 csv 函数以在 python2 和 python3 中工作(unicode -vs bytes-like object)