Python:在不使用“setrecursionlimit”的情况下酸洗高度递归对象

发布于 2024-09-02 15:38:28 字数 545 浏览 6 评论 0原文

当尝试 pickle 高度递归的树对象时,我收到了 RuntimeError: Maximum recursion Deleep Exeded 。就像这里的提问者一样。

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

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

有什么方法可以从根本上解决这个问题吗?如果只有 pickle 模块使用循环而不是递归来 pickle,我就不会遇到这个问题。也许有人知道如何在不重写 pickle 模块的情况下导致类似的事情发生?

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

I've been getting RuntimeError: maximum recursion depth exceeded when trying to pickle a highly-recursive tree object. Much like this asker here.

He solved his problem by setting the recursion limit higher with sys.setrecursionlimit. But I don't want to do that: I think that's more of a workaround than a solution. Because I want to be able to pickle my trees even if they have 10,000 nodes in them. (It currently fails at around 200.)

(Also, every platform's true recursion limit is different, and I would really like to avoid opening this can of worms.)

Is there any way to solve this at the fundamental level? If only the pickle module would pickle using a loop instead of recursion, I wouldn't have had this problem. Maybe someone has an idea how I can cause something like this to happen, without rewriting the pickle module?

Any other idea how I can solve this problem will be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无所谓啦 2024-09-09 15:38:28

我想大多数人从未使用过如此深度的递归结构。由于最简单的序列化实现是递归的,因此您只会看到它们。

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

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

如您所见,对这样一棵树进行酸洗和反酸洗在任何深度都是微不足道的。

I suppose that most people never use recursive structures of such depth. Since easiest serialization implementations are recursive, you're going to only see them.

If I were you I'd not use an openly recursive data structure here. Instead I'd number every node and use a link table that efficiently translates a number to a node with that number. Every node would refer to other nodes (e.g. its children) via that table, using numbers. A simple property would make this syntactically easy. Beyond that properties, no code dealing with tree traversal would have to change. Node constructor will have to allocate a number and put itself into the link table, which is trivial, too.

The link table might be just a list of nodes, where index into the list serves as the node number; Python lists seem to have efficient access by index. If speed of inserts is important, I'd preallocate a long enough list filled with None; it would not take too much space. If nodes stored their own numbers, this structure would be cheaply traversable in both directions.

As you see, pickling and unpickling such a tree would be trivial at any depth.

梓梦 2024-09-09 15:38:28

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

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

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

To make understanding easier, here's a complete example, with only one link to simplify it:

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

Also note that this structure can easily contain cycles and still serialize plainly.

千里故人稀 2024-09-09 15:38:28

只是不要使用递归。
创建一个具有开放节点的堆栈(列表/队列)并对其进行处理。

像这样的东西(伪代码)

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

应该可以做到

Just dont use recursion.
Make a stack (list / queue) with open nodes and process this.

Something like this (pseudo code)

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

That should do it

谈情不如逗狗 2024-09-09 15:38:28

我认为一个好的解决方案是梅内和 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

I think a good solution is a combination of Mene's and 9000's answers. Given that nodes have globally unique IDs (maybe somehow memory addresses can be used as such) you can do this. Granted this is a sloppy pseudo-implementation, but with a bit of abstraction if encapsulated in a tree class it could be very simple.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文