在 cython cdef class 中创建 python 属性时的奇怪行为

Strange behaviour when creating python attributes in cython cdef class

我们已经给出Cython代码:

cdef extern from "C_File_A.h":
    cdef struct C_Obj_A:
        pass

cdef extern from "C_File_B.h":
    cdef struct C_Obj_B:
        pass

cdef class pC_Obj_A:
    cdef const C_Obj_A * _c_self

cdef class pC_Obj_B:
    cdef const C_Obj_B * _c_self

cdef class pC_Obj_C:
    cdef const C_Obj_A * _c_a
    cdef const C_Obj_B * _c_b


cdef class Obj_A_Wrap(pC_Obj_A):
    def __init__(self, pC_Obj_C obj_c):
        self._c_self = obj_c._c_a


cdef class Obj_B_Wrap(pC_Obj_B):
    def __init__(self, pC_Obj_C obj_c):
        self._c_self = obj_c._c_b


cdef class Stack:
    cdef public pC_Obj_A obj_a
    cdef public pC_Obj_B obj_b

    def __init__(self, pC_Obj_C obj_c):
        # Working
        self.obj_a = Obj_A_Wrap(obj_c)
        self.obj_b = Obj_B_Wrap(obj_c)

        # Working
        self.obj_a._c_self = obj_c._c_a
        self.obj_b = Obj_B_Wrap(obj_c)

        # Working
        self.obj_a = Obj_A_Wrap(obj_c)
        self.obj_b._c_self = obj_c._c_b

        # Not working
        self.obj_a._c_self = obj_c._c_a
        self.obj_b._c_self = obj_c._c_b

我需要一个 python 对象 Stack,其属性可从 Python 访问,所以我已添加到 Stack class cdef public pC_Obj_A obj_acdef public pC_Obj_B obj_b.这些对象是 C 结构指针的包装器。

当我用中间包装器初始化这些对象时,即 Obj_A_Wrap 一切都很好。

当我直接初始化这些对象之一时,即 self.obj_a._c_self = obj_c._c_a 也一切正常。

obj_aobj_b 都被直接初始化时(# Not Working 代码的一部分)我的 C 库有奇怪的行为,包括 C_File_AC_File_B 和 C 结构定义。该行为类似于内存损坏,或覆盖内存中不应覆盖的某些部分。

我不知道为什么直接初始化会导致这种奇怪的行为。也许你知道?

我找到了问题的解决方案。当我试图解决这个问题时,我只打印了给定对象的 _c_self 属性,以检查指针是否已正确分配,但当我打印整个对象时,结果发现 python 对象是None 而不是声明为属性的正确对象。

print(self.obj_a, self.obj_b) # 66f000c0 66f000c0
print(f'{<int>self.obj_a._c_self:x} {<int>self.obj_b._c_self:x}') # None None

解决办法是在cdef中加入Create函数class:

cdef class pC_Obj_A:
    cdef const C_Obj_A * _c_self

    @staticmethod
    cdef Create(C_Obj_A * ptr):
        cdef pC_Obj_A result = pC_Obj_A()
        result._c_self = ptr
        return result

并像这样使用它:

cdef class Stack:
    cdef public pC_Obj_A obj_a
    cdef public pC_Obj_B obj_b

    def __init__(self, pC_Obj_C obj_c):
        self.obj_a = pC_Obj_A.Create(obj_c._c_a)
        self.obj_b = pC_Obj_B.Create(obj_c._c_b)

则打印输出为:

print(self.obj_a, self.obj_b) # <pC_Obj_A object at 0x029FF610> <pC_Obj_B object at 0x029FF620>
print(f'{<int>self.obj_a._c_self:x} {<int>self.obj_b._c_self:x}') # 2134b9c 2134c08

一切都很好!