免责声明:我不使用 Python,所以我说的有些话可能是错误的。Python专家,请随时纠正我。
好问题。我认为主要的误解(如果我什至不能这样称呼它;你如何得出你使用的思维过程是完全合理的)你有这个提示你问这个问题是:
当我写b[0] = a
的时候,并不代表a
就是在 b
。这意味着它b
包括一个指向所指向事物的引用a
。
变量a
和b
它们本身甚至不是“事物”本身,它们本身也只是指向内存中其他匿名“事物”的指针。
引用的概念是非编程领域的一个重大飞跃,因此让我们牢记这一点来逐步完成您的程序:
>>> a = [0]
您创建了一个列表,其中恰好有一些东西(暂时忽略它)。重要的是它是一个列表。该列表存储在内存中。假设它存储在内存位置 1001 中。然后,赋值=
创建一个变量a
,编程语言允许您稍后使用。此时,内存中有一些列表对象和对它的引用,您可以使用 name 访问它a
。
>>> b = [0]
这对b
. 有一个新列表存储在内存位置 1002 中。编程语言创建一个引用b
,您可以使用它来引用内存位置,进而引用列表对象。
>>> a[0], b[0] = b, a
这做了两件相同的事情,所以让我们专注于一件事情:a[0] = b
. 这做的很花哨。它首先评估等式的右侧,查看变量b
并获取内存中的相应对象(内存对象#1002),因为b
它是对它的引用。左侧发生的事情同样精彩。a
是一个指向列表的变量(内存对象#1001),但内存对象#1001 本身有许多自己的引用。与您使用的名称类似a
andb
的引用不同,这些引用具有数字索引,例如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
该语句的失败与 Python 进行比较的方式有关。当您将一个对象与其自身进行比较时,它会立即评估为真。当您比较和反对另一个对象时,它使用“魔术”来确定相等是真还是假。对于 Python 中的列表,它会查看每个列表中的每个项目并检查它们是否相等(进而使用项目自己的相等检查方法)。所以,当你尝试a == b
. 它所做的是首先挖掘 b (object #1002) 和 a (object #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
正如我在上一段中暗示的那样,这立即解析为 true,因为 Python 采用了捷径。它不需要比较列表内容,因为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
. 递归检查被跳过,比较立即返回真。
更高级别的 jibber jabber 与您的特定代码段没有直接关系:
- 由于所有的都是引用其他对象的引用,即使存在看似“无限”的嵌套,所引用的对象
a
(如我所说的对象 #1001)和所引用的对象b
(#1002)都是两者在内存中的大小相同。而且这个大小实际上非常小,因为它们都是指向各自其他内存位置的列表。
- 还值得注意的是,在不太“慷慨”的语言中,仅当两个引用指向的内存对象相同时才
==
比较两个引用与返回,即两个引用都指向内存中的同一点。Java 就是一个例子。在此类语言中出现的风格约定是在对象本身上定义一个方法/函数(对于 Java,通常称为)来进行自定义相等测试。Python 对列表进行了开箱即用的处理。我不特别了解 Python,但至少在 Ruby 中,它在某种意义上是重载的,当你这样做时,它实际上调用了一个调用的方法(你可以覆盖)。理论上,没有什么能阻止你制作true
equals()
==
someobject == otherobject
==
someobject
someobject == otherobject
返回布尔值以外的东西。