什么是列表中的省略号 [...] 的含义?

207

我在Python中进行了一些尝试。我在IDLE中使用了以下代码:

p  = [1, 2]
p[1:1] = [p]
print p

输出结果为:

[1, [...], 2]

这是什么[...]?有趣的是,我现在可以将其用作无限嵌套的列表,例如:

p[1][1][1]....

只要我想写得足够长,它仍然有效。

编辑:

  • 它在内存中是如何表示的?
  • 它有什么用途?一些实用案例会很有帮助。
  • 任何官方文档链接都将非常有用。

7
一个更简单的例子是 p = [1]; p[0] = p - arshajii
6
我认为这是 What does […] (an ellipsis) in a list mean in Python? 的一个副本,尽管这个问题(以及答案)在这个问题中更好。 - Martin Thoma
1
Dreampie很聪明
p[1:1] = [p] p 3: [1, <Recursion on list with id=3074777548>, 2] 提供精确的细节
- Rahul Gautam
@RahulGautam 没有理解 p 3: [1, <Recursion on list with id=3074777548>, 2]。你运行了什么? - Aseem Bansal
id=3074777548p 的标识符,因此很容易理解它是在引用自身。总之,非常好的问题 @Zel。 - Rahul Gautam
显示剩余3条评论
5个回答

332

这是您的代码创建的内容

输入图像描述

这是一个列表,其中第一个和最后一个元素指向两个数字(1和2),而中间的元素指向列表本身。

在Common Lisp中启用打印循环结构时,这样的对象将被打印为:

#1=#(1 #1# 2)

这句话的意思是有一个对象(用#1=标记为1),它是一个包含三个元素的向量,其中第二个元素是该对象本身(通过#1#进行反向引用)。

而在Python中,你只会得到这个结构是循环的信息,用[...]表示。

在这种特定情况下,描述不会有歧义(它指向了一个列表,但只有一个列表,所以一定是那个列表)。但在其他情况下可能存在歧义...例如:

[1, [2, [...], 3]]

后向引用可能指向外部或内部列表。可以使用以下方式创建以相同方式打印的这两个不同结构:

x = [1, [2, 3]]
x[1][1:1] = [x[1]]

y = [1, [2, 3]]
y[1][1:1] = [y]

print(x)
print(y)

然后它们将作为内存中的一部分。

在此输入图片描述


3
@csharpler:当然,你可以通过分析内容来区分这两个变量,但是它们的输出形式相同。在Common Lisp格式中,它们将分别为#(1 #1=#(2 #1# 3))#1=#(1 #(2 #1# 3)) - 6502
6
第一个使用Inkscape,第二个使用GIMP(因为我丢掉了SVG文件)。 - 6502
你是如何创建这些插图的?有使用可视化库吗? - Marcin
1
@csharpler:在Python中,您不能创建“无限列表”,因为它们实际上是可调整大小的数组,而不是链接列表。在Common Lisp中,可以使用#1=(1 . #1#)创建“无限列表”。 - 6502
2
如果你想要绘制像这样的ASCII图表,请尝试使用Asiiflow - Grijesh Chauhan
显示剩余2条评论

116
这意味着你创建了一个无限嵌套的列表,其中包含不能被打印的p内嵌p,它继续循环下去。 [...]符号是一种让你知道这一点,并告诉你它无法表示的方法!查看@6502的答案,可以看到一个漂亮的图片展示了发生了什么。
现在,关于你的编辑后的三个新条目:
  • 这个答案似乎涵盖了它
  • Ignacio的链接描述了一些可能的用途
  • 这更多是数据结构设计的话题,而不是编程语言,因此不太可能在Python的官方文档中找到任何参考。

那么它会占用无限的内存吗?我知道这是不可能的。它是如何表示的,有什么用途? - Aseem Bansal
21
@Zel:列表元素是引用。第二个元素是对列表本身的引用。 - Ignacio Vazquez-Abrams
2
Python将其识别为无限的引用循环,因此它决定缩短它,它并不真正是无限的。而且,它除了作为一个思想实验之外,并没有真正的“用处” :) - Óscar López
2
有一些无限递归结构的用途,但并不多。 - Ignacio Vazquez-Abrams
@IgnacioVazquez-Abrams 一些例子会很有用。 - Aseem Bansal

24

对于“它有什么用”的问题,这里有一个具体的例子。

图形简化是一种评估策略,有时用于解释计算机语言。这是惰性求值的常见策略,特别是函数式语言。

起点是构建代表程序将采取的“步骤”序列的图形。根据程序中使用的控制结构,这可能导致一个循环图(因为程序包含某种“永远”循环 - 或使用递归,其“深度”将在评估时间而不是在图形创建时间确定)...

为了表示这样的图形,您需要无限的“数据结构”(有时称为递归数据结构),就像您注意到的那个一样。通常会更加复杂。

如果您对这个主题感兴趣,这里有一篇关于该主题的演讲稿(除此之外还有很多其他资源):
http://undergraduate.csse.uwa.edu.au/units/CITS3211/lectureNotes/14.pdf


9
我们在面向对象编程中经常这样做。如果任何两个对象相互引用,直接或间接地,它们都是无限递归结构(或者根据你的观察方式,它们都是同一个无限递归结构的一部分)。这就是为什么在像列表这样原始的东西中很少见到这种情况——因为我们通常更善于将概念描述为相互连接的“对象”,而不是“无限列表”。
你还可以通过无限递归字典来获得...。假设您想要一个三角形角落的字典,其中每个值都是与该角落相连的其他角落的字典。您可以按以下方式设置它:
a = {}
b = {}
c = {}
triangle = {"a": a, "b": b, "c": c}
a["b"] = b
a["c"] = c
b["a"] = a
b["c"] = c
c["a"] = a
c["b"] = b

现在如果你打印triangle(或者说abc),你会看到它们中都充满了{...},因为任意两个角都相互引用。

简单的字典示例:a = {}; a ['a'] = a; print a['a']['a']['a'] - user650654
1
对我来说,它显示为“<Recursion on dict with id=___>”,而不是“...”。 - Solomon Ucko
1
@SolomonUcko 你可能正在使用IPython,它自动使用pprint来打印内容。如果你输入%pprint来切换关闭漂亮打印,它将显示... - nmclean

4
据我理解,这是一个固定点示例。
p  = [1, 2]
p[1:1] = [p]
f = lambda x:x[1]
f(p)==p
f(f(p))==p

@Zel:嗯,你必须在它之前添加OP的代码,以便声明p。 - Inkane
1
@Zel:嗯,我个人不确定它有多少帮助,但Firegun说p(因此p [1],表示为[...])是函数f的不动点。依我看,在这种情况下,这是对问题“[...]是什么?”的可能回答。 - Inkane
1
我遇到了相同的错误问题,因为在尝试更简单的p = [1]; p[0] = p示例之后才尝试了这个示例,该示例需要f = lambda x:x[0]才能正常工作。这是一个不动点的示例,但我还没有看到知道这对我们有什么用处。不动点的真正价值在于以递归或迭代方式从其他某些点到达它。如果可能的话,展示如何使用原始问题的列表结构创建Y组合器的示例将非常有帮助。 - dansalmo
“固定点组合子”(Fixed-point combinators)与函数式语言有关,但是它们是一个稍微不同的话题。它们的主要用途是生成“匿名递归函数”(用 Python 说就是“递归 lambda”)。在这里,问题是关于“递归数据结构”(Recursive data type)的。 - Sylvain Leroux
1
q = lambda: q 使一个可无限调用的lambda函数。 - whackamadoodle3000
显示剩余2条评论

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