c语言 - Cython - 替换 def __init__() 方法,因为 Cython 的 Python 函数和方法无法处理值为 0 的无符号字符数组



all.我在下面有一个 Cython 代码示例,其中我有一个无符号的 char 数组,a填充了无符号整数。当我将此数组传递到 Pythondef方法中时,包含 0 的索引之后的任何索引的值都会变得混乱。

在此示例中,由于值 0 位于第 6 个索引处,因此传入__cinit__()方法的数组中的所有后续数组索引都具有不正确的值。对于__init__()方法或使用 Python 声明def的任何函数或方法,也会发生此行为。

但是,当数组传递到任何cdefcpdef函数或方法时,数组的值是正确的。

所以,我有两个问题(请注意,我使用的是 .pyx runner 文件(:

  1. 我是否正确地将数组传递到__cinit__()方法中?有没有其他方法可以做到这一点?
  2. 或者,是否有一种 Cythonic 方法来替换def __cinit__()方法?当然,我可以使用解决方法并使用cdefcpdef方法,特别是对于我正在展示的这个简单的小示例,但我想了解是否有不同的方法......

法典:

cdef class Classical:
def __cinit__(self, unsigned char *b):
for x in range(0, 12):
print b[x], " init" # This does not work
cdef void bar(self, unsigned char *b):
for x in range(0, 12):
print b[x], " method" # This works fine
def foo(unsigned char *b):
for x in range(0, 12):
print b[x], " function" # This does not work either
cdef unsigned char a[12]
a = [
83,
12,
85,
31,
7,
0,
91,
11,
0,
12,
77,
100
]
Classical(a).bar(a)
foo(a)

输出:

83  init
12  init
85  init
31  init
7  init
0  init
0  init
0  init
0  init
0  init
0  init
0  init
83  method
12  method
85  method
31  method
7  method
0  method
91  method
11  method
0  method
12  method
77  method
100  method
83  function
12  function
85  function
31  function
7  function
0  function
100  function
0  function
0  function
0  function
0  function
0  function

def-function 的所有参数都是 Python 对象。char *(unsigned char *相同(不是 Python 对象,但是可以自动将(某些(Python 对象转换为char *。所以

def foo(char *x):
...

Cython的意思是:检查传递的Python对象是否可以转换为cdef char *,执行转换并在函数体中使用此转换的结果。

当调用带有char *的 def-函数时(另请参阅这个有点相关的 SO-post(作为参数:

cdef char a[12]
....
bar(a) # a decays to char *

Cython 执行以下操作:假设char *是一个以 null 结尾的 c 字符串,则使用自动转换到字节对象,并将此临时字节对象传递给def函数bar

这意味着在您的情况下:

  • 调用foo(a)会创建一个长度为 5 的临时字节对象(而不是 12,因为第 6 个元素是0(,前 5 个字符被复制到该对象中。
  • 在函数foo内部,这个字节对象的缓冲区的地址被获取并用作unsigned char *b,现在只有6个元素(包括尾随(,因此通过b[6]访问它是未定义的行为,并且可能以分段错误结束。

您可以通过以下方式验证ab指向不同的地址

print("Address:", <unsigned long long>(&a[0])) # or &b[0]

所以问题实际上是,当你调用foo时,不是整个数组都转换为临时的bytes-object。从/到char *的转换在Cython文档中进行了描述。在您的情况下,呼叫应该是:

foo(a[:12]) #pass the length explicitly, so cython doesn't have to depend on ''

现在打印以下内容:

83  function
12  function
85  function
31  function
7  function
0  function
91  function
11  function
0  function
12  function
77  function
100  function

对于cdef函数,情况有所不同,其中char *保持char *并且不会转换为 Python 对象。但是,__cinit__必须是def函数,因此在这种情况下通常使用cdef工厂函数,如@DavidW指出的答案所示,例如:

cdef class Classical:
...
@staticmethod
cdef Classical create(char* ptr):
obj = <Classical>Classical.__new__(Classical) # __init__ isn't called!
# set up obj while using ptr
...
return obj

显然,Classical.create只能从Cython代码中使用,但另一方面只有Cython代码有指针!

最新更新