Cython:如何从C级类型获取'actual Python type'(类型代码/dtype)



我想为使用 ctypedef 定义的内存视图分配堆栈内存,并将其作为 numpy ndarray 返回。这个问题讨论了一些分配方法,但问题是我不知道如何以编程方式将我的自定义 ctypedef 映射到分配所需的相应 numpy dtype 或 Python 类型代码。

例如:

from cython cimport view
import numpy as np
ctypedef int value_type    # actual type subject to change
# np.empty requires me knowing that Cython int maps to np.int32
def test_return_np_array(size_t N):
cdef value_type[:] b = np.empty(N, dtype=np.int32)
b[0]=12                  # from ctypedef int ^
return np.asarray(b)
# or, Cython memoryview requires the type code 'i'
def test_return_np_array(size_t N):
cdef value_type[:] b = view.array(shape=(N,), itemsize=sizeof(int), format="i")
b[0]=12                                                 # from ctypedef int ^
return np.asarray(b)

我正在使用typedef,以便我可以灵活地更改实际数据类型(例如从int更改为long long(,而无需修改所有代码。

在纯 Python 中,类型检查很容易:

value_type = int
print(value_type is int)    # True
print(value_type is float)  # False

在 numpy 中,这也可以通过将 dtype 参数化为字符串来轻松实现,例如value_type="int32"然后调用np.empty(N, dtype=value_type)。使用我的ctypedef,Cython不会编译np.empty(N, dtype=value_type),并抱怨"'value_type'不是一个常量,变量或函数标识符"。是否有可能在编译时实现这样的事情?

用户不必管理返回的内存,因此malloc不是一个选项。

我想出了一个使用C++向量的方法:<value_type[:N]>vector[value_type](N).data(),但这似乎会导致内存错误。

从C的角度来看,np.int32不是一个类型,而是一个Python对象,必须在运行时创建,不能在编译时创建。

可能最强大的方法是这个技巧(有关细节的解释,请参阅此SO问题(:

%%cython -a 
import numpy as np
def GET_SIGNED_NUMPY_TYPE():
cdef int tmp
return np.asarray(<int[:1]>(&tmp)).dtype

现在

>>> print(GET_SIGNED_NUMPY_TYPE())
int32

优点是,Cython基础设施用于映射,不需要手动容易出错的工作。


一种不那么神秘但也更容易出错的方法:您可以通过在运行时加载模块时调用的函数选择正确的类型:

%%cython
import numpy as np
ctypedef int value_type 
SIGNED_NUMPY_TYPE_MAP = {2 : np.int16, 4 : np.int32, 8 : np.int64}
SIGNED_NUMPY_TYPE = SIGNED_NUMPY_TYPE_MAP[sizeof(value_type)]
def zeros(N):
return np.zeros(N, dtype=SIGNED_NUMPY_TYPE)

现在:

>>> print(zeros(1).dtype)
int32

int更改为long long将导致np.int64被选中。

类似的方法也可以用于内存视图。


正如你所指出的,Cython 教程建议手动映射类型,例如:

ctypedef np.int32_t value_type
SIGNED_NUMPY_TYPE = np.int32

然后根据需要手动更改两者。这个简单的解决方案可能是小型程序和原型的最佳选择。但是,有一些考虑因素可能需要更强大的方法:

  • 当两个定义彼此相邻放置时,很容易看出它们必须一起更改。对于更复杂的程序,这两个定义可以放在不同的pxd或pyx文件中,然后这只是时间问题,直到它中断。

  • 只要使用固定尺寸类型(int32int64(,相应的numpy类型是显而易见的。但是,对于像intlong这样的类型,不容易分辨:

    • int仅保证至少具有 2 个字节,而不是比long个字节多。编译器可以决定选择哪种大小,可能有点担心没有保证,但是通常的嫌疑人(gcc,cland,icc和msvc(为通常的架构选择4个字节。

    • long已经是一个陷阱:对于 Linux64,gcc 选择它是 8 字节,但在 msvc 中,long只有 4 个字节长,所以不知道将使用哪个编译器,就无法提前在np.int32np.int64之间进行选择。

    • 对于long的情况,有np.int非常令人困惑,因为人们会期望np.int映射到int而不是long!然而,在 Linux64/gcc 上,np.int.itemsize是 8 个字节,但int只有 4 个字节长。另一方面,在Windows64/msvc上,np.intint都是4字节。

相关内容

最新更新