免责声明:我不使用Python,所以有些事情我说的可能是错的。 Python专家,随时纠正我。
伟大的问题。我认为中央误解(如果我甚至不能称呼它,它是完全合理的,你如何到达你所使用的思维过程)您遇到提示你要问的问题是这样的:
当我写b[0] = a
时,并不意味着a
是在b
。这意味着b
包含一个引用,指向a
指向的内容。
变量a
和b
自己本身甚至不是“事物”,它们本身也仅仅是指向内存中其他匿名“事物”的指针。
引用的概念是从非编程世界的一次重大飞跃,让我们通过你的程序与此步骤记:
>>> a = [0]
您创建碰巧有什么事情列表(忽略那现在)。重要的是它是一个列表。该列表被存储在内存中。假设它存储在内存位置1001.然后,分配=
创建一个变量a
,该编程语言允许您稍后使用。此时,在内存中有一些列表对象,并且可以使用名称a
访问它。
>>> b = [0]
这对b
做了同样的事情。有一个新的列表存储在内存位置1002.编程语言创建一个引用b
,您可以用它来引用内存位置,并依次引用列表对象。
>>> a[0], b[0] = b, a
这做了两件事是相同的,所以让我们关注一个:a[0] = b
。这样做非常花哨。它首先评估等式的右侧,看到变量b
并获取内存中的相应对象(内存对象#1002),因为b
是对它的引用。左侧发生的事情同样花哨。 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
该声明的失败与Python的比较方式有关。当你将一个对象与自己进行比较时,它立即计算结果为true。当你比较和对象到另一个对象时,它使用“魔术”来确定等式应该是真还是假。对于Python中的列表,它会查看每个列表中的每个项目,并检查它们是否相等(依次使用项目自己的等式检查方法)。所以,当你尝试a == b
。它所做的是首先挖掘b(对象#1002)和一个(对象#1001),然后意识到它们在内存中是不同的东西,所以它的递归列表检查器。它通过迭代两个列表来完成。对象#1001有一个索引为0的元素指向对象#1002。对象#1002有一个索引为0的元素指向对象#1001。因此,如果程序#1001和#1002(#1001的唯一参考点)和#1001(#1002的唯一参考点)是否相同,则程序得出结论:如果对象#1001和#1002的所有引用都指向相同的东西,一样的东西。这种平等检查永远不会停止。同样的事情会发生在任何不停止的列表中。你可以做c = [0]; d = [0]; c[0] = d; d[0] = c
和a == c
会引发同样的错误。
>>> a[0] == b
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
。递归检查被跳过,比较立即返回true。
更高级别的胡言乱语胡言乱语不直接关系到你的特定代码片段:
- 由于所有有所指的其他对象的引用,即使有这似乎是“无限”的嵌套,由
a
(正如我所说的对象#1001)引用的对象和被称为b
(#1002)的对象在内存中都是相同的大小。而且这个尺寸实际上非常小,因为它们都是指向其他存储位置的列表。
- 另外值得一注意,在更短的“大手笔”的语言,比较
==
回报true
只有如果它们指向的内存对象是在这个意义上,这两个引用指向内存中的相同点相同的两个引用。 Java就是这样的一个例子。已经出现在这种语言中的文体惯例是定义对象本身的方法/函数(对于Java,通常称为equals()
)来执行自定义相等性测试。 Python为列表开箱即用。我不太了解Python,但至少在Ruby中,==
负载过重,因为在执行someobject == otherobject
时,它实际上调用someobject
(可以覆盖)的==
方法。从理论上讲,让someobject == otherobject
返回布尔值以外的内容没有任何阻碍。
这真是一个有趣的功能。 – phimuemue
真棒问题。我非常喜欢Python的这个功能,尽管我从来没有找到它的用处。如果有人能够提出这个特性的实际应用,那将是非常好的。或者写一个模块来生成包含所有列表的列表:P – andronikus
@andronikus:http://xkcd.com/468/ –