由于Cython的Python函数和方法不能处理值为0的无符号字符数组,因此Cython替换了def\uuu init\uuu()方法

2024-04-27 04:34:57 发布

您现在位置:Python中文网/ 问答频道 /正文

全部。下面有一个Cython代码示例,其中有一个无符号字符数组,a填充了无符号整数。当我将这个数组传入Pythondef方法时,包含0的索引之后的任何索引的值都会变得混乱。你知道吗

在本例中,由于0的值位于第6个索引处,因此从数组传递到__cinit__()方法的所有后续数组索引的值都不正确。这种行为也发生在__init__()方法或任何使用Python声明的函数或方法def。你知道吗

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

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

  1. 我是否错误地将数组传递到__cinit__()方法中?还有别的办法吗?你知道吗
  2. 或者,有没有一种简单的方法来代替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

Tags: 方法inforinitdefrangefunction数组
1条回答
网友
1楼 · 发布于 2024-04-27 04:34:57

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

def foo(char *x):
   ...

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

调用带有char *(另请参见此有点相关的SO-post)作为参数的def函数时:

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

Cython执行以下操作:使用char *的自动转换,假设它是一个以null结尾的c字符串到bytes对象,并将这个临时bytes对象传递给def-函数bar。你知道吗

这意味着在你的情况下:

  • 调用foo(a)创建一个长度为5(而不是12,因为第6个元素是0)的临时bytes对象,前5个字符被复制到该对象。你知道吗
  • 在函数foo中,这个bytes对象的缓冲区的地址作为unsigned char *b使用,它现在只有6个元素(包括后面的\0),因此通过b[6]访问它是未定义的行为,可能会导致分段错误。你知道吗

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

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

所以问题实际上是,当调用foo时,并不是整个数组都转换成临时的bytes对象。从/到char *的转换在Cython-documentation中描述。在您的情况下,电话应该是:

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

现在打印以下内容:

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-工厂函数,如the answer pointed out by @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代码才有指针!你知道吗

相关问题 更多 >