如何在Python中处理递归repr()?

16
我在Python中编写了一个容器类型,并尝试编写一个健壮的__repr__方法,以正确处理包含自身的情况。例如,这是内置的list如何处理的:
>>> x = []
>>> x.append(x)
>>> repr(x)
'[[...]]'

在CPython中编写的容器类型可以使用Py_ReprEnterPy_ReprLeave实现此功能。纯Python中是否有等效的功能,还是我需要创建自己的功能呢?

2个回答

11

7

您可以创建自己的repr方法,但是如果想要做到正确性,这可能有点麻烦:您不应该在对象本身上存储“被repr标记”的标志,因为这不是线程安全的。相反,您可以存储正在进行repr的实例的线程本地集合。

一个更便宜的解决方案是依赖于内置的repr方法来处理递归,例如:

def __init__(self, *list):
    self._list= list
def __repr__(self):
    return 'mything('+repr(self._list)[1:-1]+')')

只要递归循环中的一个对象导致了Py_ReprEnter的发生,repr就无法形成完整的循环。

如何创建一个线程本地实例集合?

使用threading模块:
class MyThing(object):
    _local= threading.local()
    _local.reprs= set()

    def __repr__(self):
        reprs= MyThing._local.reprs
        sid= id(self)
        if sid in reprs:
            return 'MyThing(...)'
        try:
            reprs.add(sid)
            return 'MyThing(%r)' % self.something
        finally:
            reprs.remove(sid)

不幸的是,我必须动态计算repr字符串,因此无法像您描述的那样依赖内置的repr()。我该如何创建一个线程本地的实例集? - Daniel Stutzbach
@DanielStutzbach,为什么要包含在try/finally块中? - akaRem
1
@akaRem:self.something是一个占位符,用于涉及递归的一些更复杂的工作。在糟糕的情况下,里面的某些东西可能会导致异常发生。如果出现这种情况,我们希望确保在返回时清除reprs列表,否则它将累积实例,随着错误增加而导致越来越多的MyThing呈现为MyThing(...),即使没有递归。 - bobince
你的 self._list 实际上是一个元组,如果它只有一个元素,就会添加一个不必要的逗号... 还要注意 [1:-1] 只是切掉了 '('')',然后你立即又添加了回去 - 因此等同于 return 'mything%r' % self._list - o11c

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接