作为练习,我在指针上使用XOR将双链表放在一起,以便于每个节点仅存储一个值。我正在尝试使用对我来说是新的 Cython,我快速绘制了以下模式,不知道 cython 的功能是否会按预期工作
cdef class Node:
def __init__(self, object val, void* prev_xor_next=NULL):
self.prev_xor_next=prev_xor_next
self.val=val
def __repr__(self):
return str(self.val)
cdef class CurrentNode(Node):
def __init__(self, Node node, void* prev_ptr=NULL):
self.node=node
self.prev_ptr=prev_ptr
cpdef CurrentNode forward(self):
if self.node.prev_xor_next:
return CurrentNode((self.node.prev_xor_next^self.prev_ptr)[0], &self.node)
cpdef CurrentNode backward(self):
if self.prev_ptr:
return CurrentNode(self.prev_ptr[0], (&self.node)^(self.prev_ptr[0]).prev_xor_next)
cdef class XORList:
cdef Node first, last, current
cpdef append(self, object val):
if not first:
self.first=CurrentNode(Node(val))
self.last=self.first
self.current=self.first
else:
if last==first:
self.last=CurrentNode(Node(val))
self.first.node.prev_xor_next=(&self.last.node)^self.first.prev_ptr
self.first=self.first.node
self.last.prev_ptr=&self.first
else:
temp=CurrentNode(Node(val))
self.last.node.prev_xor_next=(&self.temp.node)^self.last.prev_ptr
self.temp.prev_ptr=&self.last
self.last=temp
cpdef reverse(self):
temp=self.last
self.last=self.first
self.first=temp
def __iter__(self):
return self
def __next__(self):
if not self.current or not self.current.forward():
self.current=CurrentNode(self.first)
raise StopIteration()
ret = self.current
self.current=self.current.forward()
return ret
def __repr__(self):
cdef str ret =''
for i in self:
ret+='<=>'+str(i)
return ret
我遇到的问题包括获取指向扩展类型的指针以及将指针传递给构造函数。我发现第二个是通过使用静态工厂方法解决的,但我在这里省略了它,希望能保持我正在尝试的模式更清晰。
我尝试用结构体替换 Node,但这让我无法将 python 对象作为 Node 的值。
我已经研究了PyObject和PyCapsule,但我对这些都没有运气,这可能仅仅是由于缺乏对它们用法的理解,尽管它们的基本目的似乎很清楚。
有没有一种方法允许使用 Cython 的这种模式?
请原谅代码中的任何逻辑错误。我还没有测试过它,这只是一个练习。
可以使用尖括号强制转换语法将扩展类型强制转换为和从指针。通常值得使用您可以cimport from cpython.object
的类型PyObject*
,但您也可以直接转到void*
:
from cpython.object cimport PyObject
cdef Node n = Node()
cdef PyObject* nptr1 = <PyObject*>n
cdef void* nptr2 = <void*>n
cdef void* nptr3 = <void*>nptr1
返回到 cdef 类型:
cdef Node new_node = <Node>nptr1
Cython不会让你做的一件事是:
# ERROR: Storing unsafe C derivative of temporary Python reference
cdef PyObject* bad = <PyObject*>Node()
它意识到新Node
几乎在制作后立即不复存在,因此指针立即无效。相比之下,nptr1
、nptr2
和nptr3
至少在未重新分配n
时有效。
请注意,您必须自己处理这些引用计数。您可以使用cimport
再次访问相关功能:from cpython.ref cimport Py_XINCREF, Py_XDECREF
。函数需要PyObject*
,因此使用它很有用。如果您打算存储这些指针之一,则需要增加引用计数,并且应该在完成引用计数后减少引用计数。
我不知道应该为您的异或列表进行什么引用计数,但您需要这样做!
这里可能还有一个更微妙的引用计数问题:如果你有循环引用(例如,如果Node
包含指向XOrList
的链接(,那么它永远不会被释放。Cython 尝试生成处理 cdef 类循环引用的函数,但它不(也不能(理解你用指针做什么,所以它们将被排除;覆盖此行为并不容易。