列表自我递归引用

11

我在Python中遇到了一个非常奇怪的事情。我尝试将列表引用添加到列表本身中。下面的代码或许能更好地展示我的意思。我正在使用IDLE编辑器(交互模式)。

>>>l=[1,2,3]
>>>l.append(l)
>>>print(l)
[1,2,3,[...]]
>>>del l[:-1]
>>>print(l)
[[...]]

目前输出结果符合预期。但当我这样做时。

y=l[:]
print(y)

对我来说,输出应该是

[[...]]

但这是

[[[...]]]

显然,它并没有创建列表的副本,而是将对列表的引用放入了y中。

y[0]是l返回True。我似乎找不到一个好的解释。有什么想法吗?

4个回答

8

两者的区别只在于列表的显示方式。也就是说,y 的值与你预期的完全相同。

列表显示方式的差异源于以下事实:与 l 不同,y 不是一个自引用列表:

l[0] is l
=> True
y[0] is y
=> False

y不是自指的,因为y没有引用y。它引用了自指的l

因此,在将列表转换为字符串的逻辑中,当处理y时,会比处理l时更深层次地检测到潜在的无限递归。


3
这是完全符合预期的。当Python打印递归列表时,它会检查正在打印的列表是否已经出现过,如果出现过,则打印[...]。理解的一个重要点是它不测试相等(如==),而是测试身份(如is)。因此,
  • 当您打印l = [l]。您有l[0] is l返回True,因此它打印[[...]]

  • 现在y = l[:]复制了l,因此y is l返回False。所以这里发生了什么。它开始打印y,因此它打印[ ??? ],其中???被替换为y[0]的打印。现在y[0]l而不是y。因此,它打印[[???]],其中???被替换为y[0][0]的打印。现在y[0][0]l,它已经出现过了。因此,它为其打印[...],最终得到[[[...]]]


你能详细说明一下吗?我在“它开始打印y: [???].....”这里不明白了。 - Guy
还有,如果我真的想要列表的副本怎么办?我知道在这种情况下我可以用y做同样的事情,但是为了讨论的目的,我能不能做到这一点? - Guy
@Sabyasachi:这样更清晰吗? - hivert
@Sabyasachi:这个第二个问题非常模糊。你在谈论哪个列表?我认为你应该考虑深拷贝和浅拷贝之间的区别(请参见https://dev59.com/r3VC5IYBdhLWcg3wykUt). - hivert
是的,谢谢,现在更清楚了。我正在谈论列表l。无论如何,现在我明白了。谢谢 :) - Guy

2

您需要拥有对象的完整副本。 您需要使用copy.deepcopy,这样您就可以看到预期的结果。

>>> from copy import deepcopy
>>> l=[1,2,3]
>>> l.append(l)
>>> print(l)
[1, 2, 3, [...]]
>>> del l[:-1]
>>> print(l)
[[...]]
>>> y=deepcopy(l)
>>> print(y)
[[...]]
>>> y[0] is l
False
>>>

当您使用切片符号复制列表时,会保留内部引用,这会导致您观察到的行为。

1
切片会生成一组项目。只有一个项目-列表“l”。因此,我们有了一个新的由一个元素组成的列表-列表“l”。

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