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)

导入如何在 IPython 中工作

如何使 readline 在 python 子进程中工作?

导入时出现pycharm错误,即使它在终端中工作

在 python 3 和 2 中工作的 Unicode 文字

pyspark 代码在控制台中工作,但不在 zeppelin 中