Cython:如何获得';实际Python类型';(类型代码/数据类型)来自C级类型
我想为使用ctypedef定义的memoryview分配堆栈内存,并将其作为numpy ndarray返回。讨论了一些分配方法,但关键是我不知道如何通过编程将自定义ctypedef映射到分配所需的相应numpy dtype或Python类型代码 例如:Cython:如何获得';实际Python类型';(类型代码/数据类型)来自C级类型,python,c++,c,numpy,cython,Python,C++,C,Numpy,Cython,我想为使用ctypedef定义的memoryview分配堆栈内存,并将其作为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 require
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
),而不必修改所有代码
在纯Python中,类型检查很容易:
value_type = int
print(value_type is int) # True
print(value_type is float) # False
在numpy中,这也可以通过将数据类型参数化为字符串来轻松实现,如value\u type=“int32”
然后调用np.empty(N,dtype=value\u type)
。使用我的ctypedef,Cython不会编译np.empty(N,dtype=value\u type)
,并抱怨“'value\u type'不是常量、变量或函数标识符”。有可能在编译时实现这样的功能吗
用户不必管理返回的内存,因此malloc
将不是一个选项
我使用C++向量来生成一个黑客:
一种不那么神秘但更容易出错的方法:您可以通过在加载模块时在运行时调用的函数来选择正确的类型:
%%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文件中,然后这只是一个时间问题,直到中断为止
- 只要使用固定大小的类型(
,int32
),相应的numpy类型就显而易见。但是对于int64
和int
等类型,很难判断:long
只能保证至少有2个字节,且不超过int
。编译器可以决定选择哪种大小,可能有点担心没有保证,但是通常的怀疑者(gcc、cland、icc和msvc)选择4字节作为通常的体系结构long
已经是一个陷阱:gcc选择Linux64为8字节,但在msvc中long
只有4字节长,因此在不知道将使用哪种编译器的情况下,不能事先在long
和np.int32
之间进行选择np.int64
- 对于
的情况,有long
,这非常令人困惑,因为人们希望np.int
映射到np.int
,而不是int
!但是在Linux64/gcc上,long
是8个字节,但是np.int.itemsize
只有4个字节长。另一方面,在Windows64/msvc上,int
和np.int
都是4个字节int
value\u type=np.int32
和ctypedef np.int32\u t value\u type\t
(可以轻松更改为value\u type=np.int64
和ctypedef np.int64\u value\u type
),基于/Cython/Includes/numpy/\uuuu init\uuuu.pxd
中的映射。你是对的,这是最简单的解决方案。我更新了我的答案,考虑了一些因素,这解释了为什么一个更健壮的方法可能值得费心。
>>> print(zeros(1).dtype)
int32
ctypedef np.int32_t value_type
SIGNED_NUMPY_TYPE = np.int32