如果一个元组是不可变的,那么为什么它可以包含可变项?

当一个可变项(如列表)被修改时,它所属的元组仍然是不可变的,这似乎是一个矛盾。


当前回答

我在这里大胆地说一下,这里的相关部分是,虽然你可以改变一个列表的内容,或者一个对象的状态,包含在一个元组中,但你不能改变的是对象或列表在那里。如果你有一些东西依赖于[3]是一个列表,即使是空的,那么我可以看到这是有用的。

其他回答

首先,“不可变”这个词对不同的人有不同的含义。我特别喜欢Eric Lippert在他的博客文章中对不变性的分类。在书中,他列举了以下几种不变性:

Realio-trulio不变性 写一次不变性 冰棒不变性 浅不变与深不变 不变的外观 观察不变性

这些元素可以以不同的方式组合在一起,从而创造出更多类型的不变性,而且我相信还有更多类型的不变性存在。您似乎对深层(也称为传递性)不可变感兴趣,在这种不可变对象中,不可变对象只能包含其他不可变对象。

这里的关键在于,深度不变性只是许许多多不变性中的一种。你可以采用你喜欢的任何一种,只要你意识到你的“不可变”概念可能与别人的“不可变”概念不同。

元组是不可变的,因为元组本身不能扩展或收缩,而不是所有包含自身的项都是不可变的。否则元组是无趣的。

我在这里大胆地说一下,这里的相关部分是,虽然你可以改变一个列表的内容,或者一个对象的状态,包含在一个元组中,但你不能改变的是对象或列表在那里。如果你有一些东西依赖于[3]是一个列表,即使是空的,那么我可以看到这是有用的。

一个原因是,在Python中没有将可变类型转换为不可变类型的通用方法(请参阅被拒绝的PEP 351,以及关于它为什么被拒绝的链接讨论)。因此,如果有这个限制,就不可能在元组中放入各种类型的对象,包括几乎任何用户创建的不可哈希对象。

字典和集合有这个限制的唯一原因是它们要求对象是可哈希的,因为它们在内部实现为哈希表。但是要注意,具有讽刺意味的是,字典和集合本身并不是不可变的(或可哈希的)。元组不使用对象的哈希,因此它的可变性无关紧要。

根据我的理解,这个问题需要重新定义为一个关于设计决策的问题:为什么Python的设计者选择创建一个可以包含可变对象的不可变序列类型?

要回答这个问题,我们必须考虑元组的用途:它们作为快速、通用的序列。考虑到这一点,很明显,为什么元组是不可变的,但可以包含可变对象。即:

Tuples are fast and memory efficient: Tuples are faster to create than lists because they are immutable. Immutability means that tuples can be created as constants and loaded as such, using constant folding. It also means they're faster and more memory efficient to create because there's no need for overallocation, etc. They're a bit slower than lists for random item access, but faster again for unpacking (at least on my machine). If tuples were mutable, then they wouldn't be as fast for purposes such as these. Tuples are general-purpose: Tuples need to be able to contain any kind of object. They're used to (quickly) do things like variable-length argument lists (via the * operator in function definitions). If tuples couldn't hold mutable objects, they would be useless for things like this. Python would have to use lists, which would probably slow things down, and would certainly be less memory efficient.

所以你看,为了实现它们的目的,元组必须是不可变的,但也必须能够包含可变的对象。如果Python的设计者想要创建一个不可变对象,以保证它“包含”的所有对象也是不可变的,那么他们就必须创建第三种序列类型。这种收益不值得额外的复杂性。