如果一个元组是不可变的,为什么它可以包含可变项?
看起来似乎存在矛盾,因为当可变项(如列表)被修改时,它所属的元组仍然保持不可变。
如果一个元组是不可变的,为什么它可以包含可变项?
看起来似乎存在矛盾,因为当可变项(如列表)被修改时,它所属的元组仍然保持不可变。
这是一个很好的问题。
关键的洞见是元组无法知道它们内部的对象是否可变。唯一使对象可变的方法就是拥有修改其数据的方法。通常情况下,没有办法检测到这一点。
另一个洞见是Python容器实际上并不包含任何内容。相反,它们保留对其他对象的引用。同样,Python变量不像编译语言中的变量;相反,变量名只是命名空间字典中的键,它们与相应的对象相关联。Ned Batchhelder在他的博客文章中很好地解释了这一点。无论哪种方式,对象只知道它们的引用计数;它们不知道这些引用是什么(变量、容器还是Python内部)。
综合这两个洞见可以解释你的谜团(为什么包含列表的不可变元组似乎在底层列表更改时发生了变化)。事实上,元组没有改变(它仍然具有与之前相同的其他对象的引用)。元组不能改变(因为它没有变异方法)。当列表更改时,元组没有收到更改的通知(列表不知道它是被变量、元组还是另一个列表所引用的)。
在这个话题上,以下是一些其他的想法,以帮助您完善对元组的认识,包括它们的工作原理和预期用途:
元组的特征不仅仅是它们的不可变性,更多的是它们的预期用途。
元组是Python将异构信息收集到一个地方的方法。例如,
s = ('www.python.org', 80)
将字符串和数字组合在一起,以便可以将主机/端口对作为套接字(一个组合对象)传递。从这个角度来看,拥有可变的组件是完全合理的。
不可变性与另一个属性hashability密切相关。但是,可哈希性并不是绝对的属性。如果元组的其中一个组件不可哈希,则整个元组也不可哈希。例如,t = ('red', [10, 20, 30])
不可哈希。
据我所知,这个问题需要重新表述为一个关于设计决策的问题:为什么Python的设计者选择创建一个不可变序列类型,它可以包含可变对象?
要回答这个问题,我们必须考虑到元组的用途:它们作为快速、通用的序列。有了这个想法,很明显为什么元组是不可变的,但可以包含可变对象。换句话说:
所以你看,为了实现它们的目的,元组必须是不可变的,但也必须能够包含可变对象。如果Python的设计者想要创建一个保证其“包含”的所有对象都是不可变的不可变对象,他们将不得不创建第三个序列类型。这种收益并不值得额外的复杂性。您不能更改其项目的id
,因此它将始终包含相同的项目。
$ python
>>> t = (1, [2, 3])
>>> id(t[1])
12371368
>>> t[1].append(4)
>>> id(t[1])
12371368
我敢断言,这里关键的部分是,虽然你可以更改包含在元组中的列表或对象的状态,但你无法改变对象或列表的存在。如果你有一些依赖于thing [3]是一个列表的东西,即使是空的,那么我认为这很有用。
元组是不可变的,这意味着元组本身不能扩展或缩小,但它包含的所有项本身并非都是不可变的。否则,元组就会很无聊。
hash()
很难检测到可哈希性,因为从*object()*继承的所有内容都是可哈希的,因此子类需要明确关闭哈希。2) 可哈希性不能保证不可变性--很容易制作可变的可哈希对象的示例。3) 元组(像Python中的大多数容器一样)只是具有对底层对象的引用--它们没有责任检查它们并推断它们。 - Raymond Hettinger