如何在Cython中创建固定长度的可变Python对象数组?

10 投票
3 回答
3279 浏览
提问于 2025-04-16 10:49

我需要一个包含Python对象的数组,用来创建一个前缀树(trie)数据结构。我想要一个固定长度的结构,像元组一样,同时又能像列表那样可变。我不想用列表,因为我希望能够确保这个列表的大小是完全正确的(如果它开始分配多余的元素,随着前缀树的增大,内存开销可能会迅速增加)。有没有什么方法可以做到这一点?我试着创建一个对象数组:

cdef class TrieNode:
    cdef object members[32]

...但是出现了错误:

Error compiling Cython file:
------------------------------------------------------------
...
cdef class TrieNode:
    cdef object members[32]
                      ^
------------------------------------------------------------

/Users/jason/src/pysistence/source/pysistence/trie.pyx:2:23: Array element cannot be a Python object

我该怎么做才能实现我的想法呢?

3 个回答

-1

这个怎么样?

class TrieNode():
   def __init__(self, length = 32):
      self.members = list()
      self.length = length
      for i in range(length):
         self.members.append(None)

   def set(self, idx, item):
      if idx < self.length and idx >= 0:
         self.members[idx] = item
      else:
         print "ERROR: Specified index out of range."
         # Alternately, you could raise an IndexError.

   def unset(self, idx):
      if idx < self.length and idx >= 0:
         self.members[idx] = None
      else:
         raise IndexError("Specified index out of range (0..%d)." % self.length)
1

如果你只需要几个固定大小的这种结构,我建议你可以创建一些类,给它们统一命名的 __slots__,其中包括一个 size 的槽位来存储大小。你需要为每个大小(槽位的数量)声明一个单独的类。然后定义一个 cdecl 函数,通过索引来访问这些槽位。虽然这样访问的性能可能没有C语言数组那种直接的地址运算快,但你可以确保槽位的数量是有限的,不会多出一个。

6

我不知道什么是最好的解决方案,但这里有一个解决方案:

from cpython.ref cimport PyObject, Py_XINCREF, Py_XDECREF

DEF SIZE = 32

cdef class TrieNode:
    cdef PyObject *members[SIZE]

    def __cinit__(self):
        cdef object temp_object
        for i in range(SIZE):
            temp_object = int(i)
            # increment its refcount so it's not gc'd.
            # We hold a reference to the object and are responsible for
            # decref-ing it in __dealloc__.
            Py_XINCREF(<PyObject*>temp_object)
            self.members[i] = <PyObject*>temp_object

    def __init__(self):
        # just to show that it works...
        for i in range(SIZE):
            print <object>self.members[i]

    def __dealloc__(self):
        # make sure we decref the members elements.
        for i in range(SIZE):
            Py_XDECREF(self.members[i])
            self.members[i] = NULL

Cython中的object是一个自动管理引用计数的PyObject *。只要你愿意自己管理这些小家伙的引用计数,你总是可以自己创建PyObject *的数组。不过,对于一些复杂的情况,这可能会让人非常头疼。

撰写回答