2012-09-01 32 views
5

我是Python新手。这里有一个关于列表的问题: 据说列表是可变的,元组是不可变的。但是,当我写:如何理解这个结果?

L1 = [1, 2, 3] 
L2 = (L1, L1) 
L1[1] = 5 
print L2 

结果是

([1, 5, 3], [1, 5, 3]) 

,而不是

([1, 2, 3], [1, 2, 3]) 

L2是一个元组和元组是不可变的。为什么当我改变L1的值时,L2的值也改变了?

+0

看起来像你创建一个视图,而不是一个深层复制? –

回答

4

该元组是不可变的,但元组内的列表是可变的。你改变了L1(列表),而不是元组。元组包含L1的两个副本,所以它们都显示更改,因为它们实际上是同一个列表。

如果一个对象是“不可变的”,这并不意味着它触及的所有东西都是不可变的。您可以将可变对象放入不可变对象内,并且这不会阻止您继续对可变对象进行变异。

4

该元组没有得到修改,它仍然包含相同的重复引用列表你给它。

你修改的列表L1),元组(或者更准确地说,不是参考到列表中的元组)。

比如你会不会能够做到

L2[1] = 5 

因为元组一成不变的,你正确的状态。

所以元组没有改变,但元组包含引用的列表被修改(因为两个条目都是对同一列表的引用,输出中的两个值都更改为5)。元组中没有值被改变。

如果您在此情况下将引用看作“指针”,可能会有所帮助。

编辑(在下面的评论基于问题的OP):

关于引用,列表和副本,也许这些例子会有所帮助:

L=range(5) 
s = (L, L[:]) # a reference to the original list and a copy 

s 
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4]) 

然后改变L [2]

L[2] = 'a' 

给出:

s 
([0, 1, 'a', 3, 4], [0, 1, 2, 3, 4]) # copy is not changed 

请注意,“2nd”列表没有更改,因为它包含副本。

现在,

L=range(5) 

我们正在创建的列表的两个副本,并给予引用的元组

s = (L[:], L[:]) 

now 
L[2] = 'a' 

不会影响什么,但原来的列表L

s 
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4]) 

希望这有帮助。

+0

谢谢。这非常有帮助。当我在定义一个变量时提到一个列表时,是否有任何通用规则告诉我什么时候该列表仅作为一个引用被包含,并且列表是否作为一个副本被包含?例如,如果我将变量A定义为L1 [1],则L1作为副本包含,但正如您所说的,当我将L2定义为[L1,L1]时,将包含L1作为参考。 – Rachaely

+0

@ user1641021如果使用切片符号(例如,L1 [:]),则您正在制作该列表的副本。我会在我的回答中多加一点,或许这会有所帮助。 – Levon

+0

@ user1641021我更新了我的答案,以解决您的一些问题,我希望它有帮助。 – Levon

1

元组包含两个引用,每个引用都是相同的列表(不是列表的副本,正如您所预料的那样)。因此,列表中的更改仍然会显示在元组中(因为元组只包含引用),但是元组本身没有被更改。因此,不可侵犯性不受侵犯。

+0

如果我写L1 = [1,2,3] A = L1 [1] L1 [1] = 5 打印A – Rachaely

+0

@ user1641021:在该片段中,首先制作一个列表并将其命名为L1。然后,将名称A绑定到L1 [1]所涉及的对象,该对象是整数2.然后将L1的第二个元素更改为5,然后打印A,它仍然是2;你“指出”在整数2,而不是“L1的第二个元素”。 – DSM

+0

但是,如果我写L1 = [1,2,3] A = L1 [1] L1 [1] = 5 打印A结果将是2而不是5.是否因为'L1 [1]'包含L1的副本,而不是L1的参考,这样即使L1改变了它的值,L1 [1]也不会? – Rachaely

7

从Python文档(http://docs.python.org/reference/datamodel.html),请注意:

不可变容器对象,它包含了一个可变 对象的引用的值当后者的价值改变时可以改变;然而容器 仍然被认为是不可变的,因为它所包含的对象集合不能被 改变。因此,不变性与具有不变的价值并不完全相同,它更加微妙。

2

你说得对,元组是不可改变的:L2是两个引用的不可变元组L1(不,因为它可能会首先出现,两个列表的元组),而L1并不是一成不变的。当你改变L1时,你不会改变L2,只是L2引用的对象。

2

使用deepcopy的代替=

从拷贝导入deepcopy的
L2 = deepcopy的(L1)

0

元组是不可变的手段只有一两件事 - 一旦你构造一个元组,不可能修改它。另一方面,列表可以添加元素,从中删除元素。但是,元组和列表都与它们包含的元素有关,但与这些元素的内容无关。

在Python中,这与元组或列表无关,当你添加一个简单的值,比如一个int值时,它被表示为is,但是像列表,元组或其他任何类的复杂值类型的对象是总是存储为参考。

如果你要你的元组转换为set(),你会得到那可能会让你大吃一惊的错误消息,但鉴于上述,应该是有意义:

>>> L=range(5) 
>>> s = (L, L[:]) # a reference to the original list and a copy 
>>> set(1, 2, s) 
>>> set((1, 2, s)) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: unhashable type: 'list' 

由于值set绝决一旦它们被添加到集合中,则包含在不可变元组s内的任何可变值都将提升TypeError

+0

这使得看起来'int'不是'通过引用存储'的方式与“复数值”相同,而是“表示为”。但是'int'与'tuple'完全相同。 – DSM