自我引用列表

4

假设你做以下操作:

a = [1]
a[0] = a

你最终得到 a[[...]] 相等。这是怎么回事呢?这个隐式定义的无限链如何将指向 aa 改为 [[...]] 呢?

3
那怎么会是暗示呢?你清楚自己在做什么。 - jamylak
3
[...] 是 Python 用来表示自引用字符串的方式。请参见 https://dev59.com/BW865IYBdhLWcg3wnP6a。 - Stecman
3个回答

9

你还没见过:

>>> a = []
>>> a[:] = [a] * 4
>>> a
[[...], [...], [...], [...]]

如果您对CPython中的工作方式感兴趣,请参见listobject.c中的list_repr和类似的函数。基本上,任何可能打印自引用对象的函数在打印之前都会调用Py_ReprEnter,并在完成后调用Py_ReprLeave。(有关这些函数的定义,请参见object.c)。前者检查对象是否在线程本地对象栈中找到(如果没有,则将其推送);后者从堆栈中弹出对象。因此,如果Python正在打印列表并发现该列表在栈中,那么这必须意味着这是一个自引用列表,并且应该缩写该列表,以避免无限循环:
 i = Py_ReprEnter((PyObject*)v);
 if (i != 0) {
     return i > 0 ? PyString_FromString("[...]") : NULL;
 }

 // ...

 Py_ReprLeave((PyObject *)v);
 return result;

4

这个列表包含对它本身的引用。当你打印这个列表时,[[...]] 是如何呈现的。

实现过程会设法确保不会陷入无限递归。它通过将已经被打印的对象的引用渲染为[...]来实现。

这使得它也可以处理间接自引用:

>>> a = []
>>> b = [a]
>>> a.append(b)
>>> a
[[[...]]]
>>> b
[[[...]]]

如果您真的很好奇,可以学习CPython的源代码。在Python 2.7.3中,相关的代码位于Objects/listobject.c


1
只是想为 OP 强调一些内容,这不是无限递归,因为它只是一个 引用,它并没有尝试通过值传递自己。 - jamylak
2
@jamylak:没错。我的意思是,一个试图盲目遍历a的方法最终会陷入无限递归。 - NPE
哦,我或许应该把那个评论放在问题下面,我不是在引用你的回答。 - jamylak

2
我们这里有 a == [a],理论上来说,a 应该被打印为一个只包含一个元素的列表,即它本身也是一个只包含一个元素的列表,依此类推。或者说,在打印时,会有无限数量的 [,后面跟着无限数量的 ]。但实际上我们得到的是 [[...]],这是因为 Python 试图帮助我们,而不是真正地打印出无限数量的 [

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