我是 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
也会改变?
在 Python 文档 (http://docs.python.org/reference/datamodel.html) 中,请注意:
包含对可变对象的引用的不可变容器对象的值可以在后者的值更改时更改;但是容器仍然被认为是不可变的,因为它包含的对象集合不能更改。因此,不变性与具有不可更改的值并不严格相同,它更微妙。
元组是不可变的,但元组内的列表是可变的。您更改了 L1(列表),而不是元组。元组包含 L1 的两个副本,因此它们都显示了更改,因为它们实际上是同一个列表。
如果一个对象是“不可变的”,那并不意味着它所接触的一切都是不可变的。您可以将可变对象放在不可变对象中,这不会阻止您继续改变可变对象。
元组没有被修改,它仍然包含与您给它的列表相同的重复引用。
您修改了一个列表( 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
请注意,“第二个”列表没有改变,因为它包含一个副本。
现在,
L=range(5)
我们正在创建列表的两个副本并提供对元组的引用
s = (L[:], L[:])
now
L[2] = 'a'
除了原始列表 L 之外不影响任何内容
s
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
希望这会有所帮助。
你说得对,元组是不可变的:L2 是对 L1 的两个引用的不可变元组(不像它可能首先出现的那样,是两个列表的元组),并且 L1 不是不可变的。当你改变 L1 时,你并没有改变 L2,只是改变了 L2 引用的对象。
使用deepcopy而不是=
:
从复制导入 deepcopy
L2 = deepcopy(L1)
元组包含两个引用,每个引用指向同一个列表(不是列表的副本,如您所料)。因此,列表中的更改仍将显示在元组中(因为元组仅包含引用),但元组本身不会更改。因此,它的不变性没有被违反。
元组不可变只意味着一件事——一旦你构造了一个元组,就不可能修改它。另一方面,列表可以添加元素,删除元素。但是,元组和列表都关心它们包含的元素,而不关心这些元素是什么。
在 Python 中,这与元组或列表无关,当您添加一个简单值(如 int)时,它会按原样表示,但任何复杂值(如列表、元组或任何其他类类型对象)都是始终作为参考存储。
如果您要将元组转换为 a 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'
由于 a 的值set
一旦添加到集合中就永远不会改变,因此包含在不可变元组中的任何可变值s
都会 raise TypeError
。