如果一个元组是不可变的,那么为什么它可以包含可变项呢?
似乎矛盾的是,当可变项(例如列表)确实被修改时,它所属的元组保持不变。
这是一个很好的问题。
关键的见解是,元组无法知道其中的对象是否可变。使对象可变的唯一方法是拥有一种更改其数据的方法。通常,无法检测到此情况。
另一个见解是Python的容器实际上不包含任何东西。相反,它们保留对其他对象的引用。同样,Python的变量与编译语言中的变量不同。相反,变量名只是名称空间字典中的键,它们与对应的对象相关联。Ned Batchhelder在他的博客文章中很好地解释了这一点。无论哪种方式,对象仅知道其引用计数。他们不知道这些引用是什么(变量,容器或Python内部函数)。
这两种见解共同解释了你的奥秘(为什么当基础列表更改时,包含列表的不可变元组似乎也会更改)。实际上,元组没有变化(它对其他对象的引用与以前相同)。元组无法更改(因为它没有变异方法)。当列表更改时,没有通知元组更改(列表不知道它是由变量,元组还是其他列表引用)。
当我们讨论该主题时,还有一些其他想法可以帮助你完善关于元组是什么,它们如何工作以及其预期用途的思维模型:
元组的特征较少在于其不变性,而其特征在于其预期目的。 元组是Python在一个屋顶下收集异构信息的一种方式。例如, s = ('www.python.org', 80) 将字符串和数字组合在一起,以便主机/端口对可以作为套接字(复合对象)传递。从这个角度来看,具有可变的组件是完全合理的。
s = ('www.python.org', 80)
不变性与另一个特性,即哈希性密切相关。但是哈希性不是绝对的属性。如果元组的组成部分之一不可哈希,则整个元组也不可哈希。例如,t = ('red', [10, 20, 30])不可散列。
t = ('red', [10, 20, 30])
最后一个示例显示了一个包含字符串和列表的2元组。元组本身是不可变的(即,它没有任何更改其内容的方法)。同样,字符串是不可变的,因为字符串没有任何突变方法。列表对象确实具有变异方法,因此可以对其进行更改。这表明可变性是对象类型的属性-有些对象具有突变方法,有些则没有。这不会因为对象被嵌套而改变。
记住两件事。首先,不变性不是魔术,而是缺少突变方法。其次,对象不知道哪些变量或容器引用了它们-它们仅知道引用计数。