Python中哪些是不可变的?

3 投票
2 回答
1976 浏览
提问于 2025-04-16 15:42

我正在尝试弄清楚以下对象在Sage中是否是不可变的(因为Sage是基于Python的,所以我认为如果在Python中是不可变的,那么在Sage中大多数情况下也会是不可变的)。

下面是对象e、f、g和i。

class e: pass
f = e()
g = pi # (g's "type" in Sage is symbolic expression. It's supposed to be 3.1415....etc)
i = lambda x: x*x

我了解到e是一个类,这意味着它是可变的(不可变的类有意义吗?难道所有类都可以被修改吗?)。因为f是一个类的实例,所以我猜它也是可变的,因为类是可变的。

在Python中,数字是不可变的,所以g应该也是不可变的,尽管它是一个无理数。

最后,i是一个函数,这意味着它应该是可变的?

我不太明白不可变性的概念。对于一个函数来说,不可变意味着什么?对于一个类来说,不可变又意味着什么呢?

2 个回答

3

正式来说,一个对象是可变的,如果它的可以改变,但身份不变。

列表是可变的,所以某个特定的实例的值可以随着时间的推移而改变:

>>> x = orig_x = []
>>> x == []
True
>>> x += [1]
>>> x == []      # The value of x has changed
False
>>> x is orig_x  # But the identity remains the same
True

但是,数字是不可变的,所以它们的值不能改变。相反,变量必须更新为指向一个完全不同的对象:

>>> x = orig_x = 1
>>> x == 1
True
>>> x += 1
>>> x == 1        # Again, the value of x has changed
False
>>> x is orig_x   # But now the identity has changed as well
False

不可变性是一个重要的概念,因为知道一个对象的值不能改变,可以让你对它做出一些假设(例如,dict 实际上需要不可变的键,而 setfrozenset 需要不可变的成员,因为对象的值会影响它在数据结构中的存储方式。如果允许可变的条目,它们在存储后被修改可能会导致它们放错地方)。

与普遍的看法相反,用户定义的类如果没有重写相等的定义,技术上来说是不可变的。这是因为用户定义类的默认“值”定义就是 id(self)。当一个对象的值就是它的身份时,显然它们不可能随着时间的推移而不同,因此这个对象不算“可变”。

非正式来说?大多数人用一种直观的“我能改变它吗?”的定义,类似于 Gareth McCaughan 的回答。这和正式定义的基本思想是一样的,只是用更广泛的“值”含义,而不是技术上关于相等性检查的定义。

7

e 是可变的。比如,你可以给这个类添加一个新方法:e.foo = lambda self,x: x

f 也是可变的。你可以给这个类的实例添加一个新属性:f.x = 99

g 是不可变的。你不能对它做任何修改。

i 不是不可变的。你可以对它做一些“坏事”:比如执行 i.func_code = (lambda x: 123).func_code,这样之后 i(10) 的结果就会变成 123,而不是 100。(你也可以做一些更合理的事情。比如执行 i.__doc__ = "这个函数返回它参数的平方.",这样在使用 help(i) 时就能得到更有帮助的信息。)

一个对象是可变的,如果你可以对这个对象做一些操作,从而改变它未来的行为。比如,你不能改变数字 10 的行为;但你可以改变一个函数对象、一个类、一个类的实例,或者一个列表的行为。(不过元组就不行了。一旦创建了元组,它就会一直保持不变。)

撰写回答