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

标签 python c++ c numpy cython

我想为使用 ctypedef 定义的内存 View 分配堆栈内存,并将其作为 numpy ndarray 返回。 This question讨论了一些分配方法,但问题是我不知道如何以编程方式将自定义 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,以便可以灵活更改实际数据类型(例如从 intlong 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++ vector 的 hack:<value_type[:N]>vector[value_type](N).data() ,但这似乎会导致内存错误。

最佳答案

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

可能最强大的方法就是这个技巧(有关详细信息的说明,请参阅此 SO-question ):

%%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

类似的方法也可用于内存 View 。


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

ctypedef np.int32_t value_type
SIGNED_NUMPY_TYPE = np.int32

然后根据需要手动更改两者。这个简单的解决方案可能最适合较小的程序和原型(prototype)。然而,有一些考虑因素可能需要更强大的方法:

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

  • 只要使用固定大小类型(int32int64),对应的numpy类型是显而易见的。然而,对于像 intlong 这样的类型,很难区分:

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

    • long 已经是一个陷阱:gcc 对于 Linux64 选择它为 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 个字节。

关于python - Cython:如何从 C 级类型获取 'actual Python type'(类型代码/dtype),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50981091/

相关文章:

python - 使用jupyter在python3中用字符串替换标准输入

c++ - 建库失败 : file format not recognized; treating as linker script

C++:char test[100] vs array<char, 100> vs string

c++ - 这个私有(private)变量 "not declared in this scope"怎么样?

将指针转换为 float 或指向带有指针参数的函数

c - 从不兼容的指针类型进行赋值 [-Werror]

python - 如何只添加一个文件到 zip 而不是在 Python 中指向它的文件夹路径?

python - 从 Jinja 表达式中调用的函数中的字典访问值

python CFFI : Build single module from multiple source files

c - malloc 一个 char[][]