Python Cython“;内存有效的双链表“;

Python Cython“;内存有效的双链表“;,python,pointers,linked-list,cython,Python,Pointers,Linked List,Cython,作为练习,我在指针上使用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

作为练习,我在指针上使用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
cdef类节点:
定义初始值(self,object val,void*prev\u xor\u next=NULL):
self.prev\u xor\u next=prev\u xor\u next
self.val=val
定义报告(自我):
返回str(self.val)
cdef类CurrentNode(节点):
def uuu init uuuu(self,Node Node,void*prev_ptr=NULL):
self.node=node
self.prev_ptr=prev_ptr
cpdef CurrentNode转发(自):
如果self.node.prev\u xor\u next:
返回CurrentNode((self.node.prev\u xor\u next^self.prev\u ptr)[0],&self.node)
cpdef CurrentNode向后(自):
如果self.prev\u ptr:
返回CurrentNode(self.prev_ptr[0],(&self.node)^(self.prev_ptr[0]).prev_xor_next)
cdef类XORList:
cdef节点第一个、最后一个、当前
cpdef追加(自身、对象val):
如果不是首先:
self.first=CurrentNode(节点(val))
self.last=self.first
self.current=self.first
其他:
如果last==first:
self.last=CurrentNode(节点(val))
self.first.node.prev\u xor\u next=(&self.last.node)^self.first.prev\u ptr
self.first=self.first.node
self.last.prev_ptr=&self.first
其他:
温度=当前节点(节点(val))
self.last.node.prev\u xor\u next=(&self.temp.node)^self.last.prev\u ptr
上一次自我温度=&self.last
self.last=temp
cpdef反转(自):
温度=自上次
self.last=self.first
自我优先=临时
定义(自我):
回归自我
定义下一个(自我):
如果不是self.current或不是self.current.forward():
self.current=CurrentNode(self.first)
提升停止迭代()
ret=自电流
self.current=self.current.forward()
回程网
定义报告(自我):
cdef str ret=“”
对于我自己:
ret+=''+str(一)
回程网
我面临的问题包括获取指向扩展类型的指针和将指针传递给构造函数。我发现第二个问题是通过使用静态工厂方法解决的,但我在这里忽略了这一点,希望能保持我正在尝试的模式更清晰

我尝试过用结构替换节点,但这使我无法将python对象作为节点的值

我已经研究过PyObject和PyCapsule,但是我对它们都没有什么运气,这可能是因为对它们的用法缺乏了解,尽管它们的基本用途看起来很清楚

有没有一种方法可以使用Cython实现这种模式


请原谅代码中的任何逻辑错误。我还没有测试它,这只是一个练习。

您可以使用尖括号转换语法在指针之间转换扩展类型。通常值得使用类型
PyObject*
,您可以
cimport从cpython.object
,但也可以直接转到
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
来自cpython.object cimport PyObject
cdef节点n=节点()
cdef PyObject*nptr1=n
cdef无效*nptr2=n
cdef无效*nptr3=nptr1
要返回到cdef类型,请执行以下操作:

cdef Node new_node = <Node>nptr1
cdef节点新建\u节点=nptr1
Cython不会让你做的一件事是:

# ERROR: Storing unsafe C derivative of temporary Python reference
cdef PyObject* bad = <PyObject*>Node()
#错误:存储临时Python引用的不安全C派生
cdef PyObject*坏=节点()
它意识到新的
节点
几乎一创建就不存在,因此指针立即无效。相反,
nptr1
nptr2
nptr3
至少在
n
未重新分配的情况下有效


请注意,您必须自己处理这些引用计数。您可以再次使用
cimport
访问相关功能:
来自cpython.ref cimport Py_XINCREF,Py_XDECREF
。函数采用
PyObject*
,因此使用它很有用。如果您打算存储这些指针中的一个,那么您需要增加引用计数,并且在使用完它之后应该减少引用计数

我不知道应该为xor ed列表执行什么样的引用计数,但您需要这样做


这里还有一个潜在的更微妙的引用计数问题:如果您有循环引用(例如,
Node
包含指向
XOrList
的链接),那么它将永远不会被释放。Cython试图生成处理cdef类的循环引用的函数,但是它不(也不能)理解你在用指针做什么,所以它们将被排除在外;重写此行为并不容易。

您可以使用尖括号转换语法在指针之间转换扩展类型。通常值得使用类型
PyObject*
,您可以
cimport从cpython.object
,但也可以直接转到
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
来自cpython.object cimport PyObject
cdef节点n=节点()
cdef PyObject*nptr1=n
cdef无效*nptr2=n
cdef无效*nptr3=nptr1
要返回到cdef类型,请执行以下操作:

cdef Node new_node = <Node>nptr1
cdef节点新建\u节点=nptr1
Cython不会让你做的一件事是:

# ERROR: Storing unsafe C derivative of temporary Python reference
cdef PyObject* bad = <PyObject*>Node()
#错误:存储临时Python引用的不安全C派生
cdef PyObject*坏=节点()
它意识到新的
节点
几乎一创建就不存在,因此指针立即无效。相反,
nptr1
nptr2
nptr3
至少在
n
未重新分配的情况下有效


请注意,您必须自己处理这些引用计数。您可以使用
cimport
再次访问相关功能: