在尝试挑选高度递归的树对象时,我一直在获得RuntimeError: maximum recursion depth exceeded
。很像this asker here。
他通过使用sys.setrecursionlimit
设置递增限制来解决他的问题。但我不想这样做:我认为这更像是一种解决方法,而不是一种解决方案。因为我希望能够腌制树木,即使它们中有10,000个节点。 (它目前在200左右失败。)
(另外,每个平台的真正递归限制都不同,我真的想避免打开这种蠕虫。)
有没有办法在基础层面解决这个问题?如果只有pickle模块使用循环而不是递归来腌制,我就不会遇到这个问题。也许有人知道如何在不重写pickle模块的情况下导致这样的事情发生?
任何其他想法如何解决这个问题将不胜感激。
答案 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