当前位置: 首页 > 面试题库 >

无限嵌套列表中到底发生了什么?

韦星文
2023-03-14
问题内容

可以在Python中创建无限的嵌套列表。这很明显,尽管不受欢迎并且绝对没有用,但这是一个已知事实。

>>> a = [0]
>>> a[0] = a
>>> a
[[...]]
>>> a[0] == a
True

我的问题是,这里发生了什么:

>>> a = [0]
>>> b = [0]
>>> a[0], b[0] = b, a
>>> a
[[[...]]]
>>> b
[[[...]]]
>>> a == b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp
>>> a[0] == b
True
>>> a[0][0] == b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp
>>> a[0][0][0] == b
True
>>>

当我试图理解它时,会更深入地了解我,就像我的大脑将要爆炸一样。我看到,a包含b,其中包含a依此类推…

现在我对这个问题提出疑问。我们这里真的有两个列表,还是只有一个?这样的事情如何存储在内存中?使程序员实现如此奇怪的目标的目的可能是什么?

拜托,不要把这个问题当回事。并且不要忘记,编程有时会很有趣。


问题答案:

免责声明:我不使用Python,所以我说的某些话可能是错误的。Python专家,随时纠正我。

好问题。我认为中心的误解 (如果我什至不能这样称呼;您到达所用思维过程的方式是完全合理的) ,这会提示您提出以下问题:

当我写的b[0] = a,这并不意味着a b。这意味着其中b包含指向所指事物的参考a

变量ab它们本身甚至都不是“事物”,它们本身也只是指向内存中其他匿名“事物”的指针。

引用的概念是从非编程世界的一大飞跃,因此,请牢记这一点,逐步完成程序:

>>> a = [0]

您创建一个列表,其中恰好有东西(暂时忽略该列表)。重要的是列表。该列表将存储在内存中。假设它存储在内存位置1001中。然后,赋值=创建一个变量a,编程语言允许您稍后使用该变量。此时,内存中存在一些列表对象,并可以使用name对其进行引用a

>>> b = [0]

这样做与相同b。有一个新列表存储在内存位置1002中。编程语言创建了一个引用b,您可以使用该引用来引用内存位置,进而引用该列表对象。

>>> a[0], b[0] = b, a

这样做有两件事是完全相同的,所以让我们专注于一个:a[0] = b。这确实很花哨。b由于b它是对它的引用,因此它首先计算等式的右侧,查看变量并获取内存中的相应对象(内存对象#1002)。同样,在左侧发生的事情也很奇特。a是指向列表(内存对象#1001)的变量,但是内存对象#1001本身具有许多引用。这些引用具有数字索引,而不是您使用的那些引用,例如,a和。所以,现在,这是b``0``a拉起一堆索引引用的内存对象#1001,然后转到索引为0的引用(以前,此引用指向实际编号0,这是您在第1行所做的操作),然后重新指向该引用(也就是说,在存储对象#1001中,第一个也是唯一的参考是等式右边的东西求值。因此,现在,对象#1001的第0个引用指向对象#1002。

>>> a
[[[...]]]
>>> b
[[[...]]]

这只是编程语言的幻想。当您只是要求它评估时a,它会拉出内存对象(位置#1001的列表),使用自身的魔术来检测它是无限的,并如此渲染自身。

>>> a == b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp

b。它的工作是首先挖掘b(对象#1002)和a(对象#1001),然后意识到它们在内存中是不同的,因此转到其递归列表检查器。它通过遍历两个列表来做到这一点。对象#1001具有一个索引为0的元素,该元素指向对象#1002。对象#1002具有一个索引为0的元素,该元素指向对象#1001。因此,程序得出以下结论:如果对象#1001和#1002的所有引用都指向同一事物,则它们相等;如果#1002(#1001的唯一引用指向)和#1001(#1002的唯一引用指向),则认为是错误。同样的事情。这种平等检查永远不会停止。在任何不停止的列表中都会发生同样的事情。您可以这样做,c
= [0]; d = [0]; c[0] = d; d[0] = c并且a == c`会引发相同的错误。

>>> a[0] == b
True

正如我在上一段中所暗示的那样,由于Python采用了快捷方式,因此这立即变为true。它不需要比较列表内容,因为a[0]指向对象#1002和b对象#1002。Python检测到它们在字面意义上是相同的(它们是相同的“事物”),甚至不用费心检查内容。

>>> a[0][0] == b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp

这回到错误状态,因为a[0][0]最终指向对象#1001。身份检查失败,然后退回递归内容检查,该检查永远不会结束。

>>> a[0][0][0] == b
True

再次a[0][0][0]指向对象#1002 b。跳过递归检查,比较立即返回true。

与您的特定代码段不直接相关的高级jibber jabber:

  • 由于所有引用都指向其他对象,因此即使存在“无限”嵌套,由a(如我称为对象#1001)所引用的对象和由b(#1002)所引用的对象也是两者在内存中的大小相同。实际上,该大小非常小,因为它们都是指向各个其他内存位置的列表。
  • 还需要注意的是,在“通用”语言较少的情况下, 仅* 当两个引用指向的内存对象相同 时才 将两个引用与==返回值进行比较,即两个引用都指向内存中的同一位置。Java就是一个例子。此类语言中出现的风格惯例是在对象本身上html" target="_blank">定义方法/功能(对于Java,通常称为)以进行自定义相等性测试。Python为列表开箱即用。我不知道Python的特别,但至少在Ruby中,在这个意义上,当你做的是超载,它实际上调用了一个名为方法上(也可以覆盖)。从理论上讲,没有什么可以阻止您true *equals()``==``someobject == otherobject``==``someobject``someobject == otherobject返回布尔值以外的其他值。


 类似资料:
  • 问题内容: 我想了解嵌套列表的理解。下面,我列出了一个列表理解表达式及其for循环等效项。 我不知道我的理解是否正确。 例如, 相当于 如果可以概括一下,我猜 表格可以翻译为以下内容。(我希望我是对的) 对于更简单的情况, 等于 而, 等于 我问了一个类似的问题,即用于复杂列表理解 的循环表达式的等效项。那里给出的答案是在理解了表单内部的内容之后重构了表单。 我想知道它是如何系统地工作的,因此我可

  • 问题内容: Go函数和方法可以返回多个值。 总和,prod:= learningMultiple(10,50) 它类似于返回元组吗? 我来自红宝石土地,我可以返回一个数组和一个 问题答案: 我们可以轻松地查看一些已编译的代码,以确认幕后情况。 考虑以下代码段: 如果我们反汇编所创建的ELF二进制文件,您将看到类似以下内容(内联被禁用,因此我们可以看到正在发生的调用): 因此,只需将结果字节放入堆栈

  • 我正在学习套接字编程,服务器套接字让我感到困惑。我为服务器套接字写了两个场景,请看一下: null 如果我的一个假设是正确的,那会回答下面的问题吗?或者,我的两种设想都是错误的。如果你能指导我找到正确的答案,或者至少是一些相关的文本来学习,我将非常感谢。

  • 问题内容: 好的,现在我这里有一个无序列表: 基本上,我只想将此数据转换为JSON实体。我想在 Jquery中 完成此操作,而且我觉得我很难过。上面的列表只是一个例子,实际上,我的列表理想情况下将有更多的孩子,并且深度可能是n级(意思是,它将有孙子孙的孙子……或更多)我失去了无数数小时的睡眠,我不认为我会去任何地方:( 我想提取这些东西:锚点内的文本,锚点的url和锚点的标题,并将它们放到JSON

  • 问题内容: 试图了解如何在Firebase中实现嵌套列表。 可以解决的问题:1-to-N消息传递系统,对于每个消息,您希望维护一个已接收和阅读该消息的用户列表。 阅读了“ Firebase中阵列的最佳实践”。尝试避免使用数组,因为我要同时进行写操作,而在这里看来它们并不是一个好选择。 当前试图通过在每个消息下存储子树来实现此目的,每个子树是已接收,读取或以其他方式对消息 Y 执行某些操作 X 的用

  • 问题内容: 谁能告诉我如何在嵌套列表中调用索引? 通常我只写: 但是如果我有一个带有嵌套列表的列表,如下所示: 我想分别浏览每个索引? 问题答案: 如果您确实需要索引,则可以按照内部列表再次执行以下操作: 但是遍历列表本身是更pythonic的: 如果您确实需要索引,也可以使用: