Python:在不使用`setr​​ecursionlimit`的情况下腌制高递归对象

时间:2010-05-26 12:22:00

标签: python recursion pickle

在尝试挑选高度递归的树对象时,我一直在获得RuntimeError: maximum recursion depth exceeded。很像this asker here

他通过使用sys.setrecursionlimit设置递增限制来解决他的问题。但我不想这样做:我认为这更像是一种解决方法,而不是一种解决方案。因为我希望能够腌制树木,即使它们中有10,000个节点。 (它目前在200左右失败。)

(另外,每个平台的真正递归限制都不同,我真的想避免打开这种蠕虫。)

有没有办法在基础层面解决这个问题?如果只有pickle模块使用循环而不是递归来腌制,我就不会遇到这个问题。也许有人知道如何在不重写pickle模块的情况下导致这样的事情发生?

任何其他想法如何解决这个问题将不胜感激。

4 个答案:

答案 0 :(得分:2)

我认为大多数人从不使用这种深度的递归结构。由于最简单的序列化实现是递归的,因此您只能看到它们。

如果我是你,我不会在这里使用公开的递归数据结构。相反,我会为每个节点编号并使用一个链接表,该链接表可以有效地将数字转换为具有该数字的节点。每个节点都使用数字通过该表引用其他节点(例如其子节点)。一个简单的属性会使这在语法上变得容易。除了这些属性之外,没有处理树遍历的代码必须改变。节点构造函数必须分配一个数字并将自己放入链接表中,这也是微不足道的。

链接表可能只是一个节点列表,其中列表中的索引用作节点号; Python列表似乎可以通过索引进行高效访问。如果插入速度很重要,我会预先分配一个填充了None的足够长的列表;它不会占用太多空间。如果节点存储了他们自己的数字,那么这个结构在两个方向上都可以廉价地遍历。

正如你所看到的,在任何深度上腌制和去除这样一棵树都是微不足道的。

答案 1 :(得分:2)

为了使理解更容易,这里有一个完整的例子,只有一个链接来简化它:

class Node(object):
  linker = [] # one list for all Node instances
  def __init__(self, payload):
    self.payload = payload
    self.__next = None
    self.__index = len(self.linker)
    self.linker.append(self)
  #
  def getNext(self):
    if self.__next is not None:
      return self.linker[self.__next]
  #
  def setNext(self, another):
    if another is not None:
      self.__next = another.__index
    else:
      self.__next = None
  #
  next = property(getNext, setNext)
  #
  def __str__(self):
    return repr(self.payload)


a = Node("One")
b = Node("Two")
c = Node("Three")

b.next = c
a.next = b

# prints "One" "Two" "Three"
print a, a.next, a.next.next

另请注意,此结构可以轻松地包含循环并仍然可以明确地序列化。

答案 2 :(得分:1)

不要使用递归。 使用开放节点创建堆栈(列表/队列)并处理它。

像这样(伪代码)

stack.add(root)
while not list.empty:
    current = stack.pop
    // process current
    for each child of current:
        stack.add(child)

应该这样做

答案 3 :(得分:1)

我认为一个好的解决方案是Mene和9000的答案。鉴于节点具有全局唯一ID(可能以某种方式可以使用内存地址),您可以执行此操作。虽然这是一个草率的伪实现,但如果封装在树类中有一些抽象,它可能非常简单。

def all_nodes(node): # walk the tree and get return all nodes as a list
    if node:
        nodes = []
        for child in node.children:
            for sub_child in all_nodes(child):
                nodes.append(sub_child)
        return nodes
    return []


class Node(object):
    def __init__(self, children, id):
        self.children = children
        self.id = id

    def __getstate__(self): #when pickling translate children into IDs
        tmp = self.__dict__.copy()
        children_ids = []
        for child in tmp['children']:
            children_ids.append(child.id)
        tmp['children_ids'] = children_ids
        return tmp


lookup = dict()


for node in all_nodes(rootNode): # put all nodes into a dictionary
    lookup[node.id] = node
#then pickle the dictionary
#then you can unpickle it and walk the dictionary
for id, node in lookup:
    del node.children
    node.children = []
    for child in node.children_ids:
        node.children.append(lookup[child])
#and three should now be rebuilt