不可变类型与可变类型

209 投票
20 回答
287545 浏览
提问于 2025-04-17 05:54

我对什么是不可变类型有点困惑。我知道 float 对象被认为是不可变的,我书里的一个例子是这样的:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

这是因为类的结构或层次关系,所以 float 被认为是不可变的吗?也就是说,float 在类的顶部,并且是它自己的方法调用。类似于这个例子(尽管我书上说 dict 是可变的):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

而可变的东西在类里面有方法,像这个例子:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

另外,对于最后一个 class(SortedKeyDict_a),如果我传递这种类型的集合给它:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

而不调用 example 方法,它会返回一个字典。SortedKeyDict__new__ 标记为错误。我尝试把整数传给 RoundFloat 类,使用 __new__,但没有标记任何错误。

20 个回答

117

常见的不可变类型:

  1. 数字:int()(整数)、float()(浮点数)、complex()(复数)
  2. 不可变序列:str()(字符串)、tuple()(元组)、frozenset()(冻结集合)、bytes()(字节)

常见的可变类型(几乎所有其他类型):

  1. 可变序列:list()(列表)、bytearray()(字节数组)
  2. 集合类型:set()(集合)
  3. 映射类型:dict()(字典)
  4. 类和类实例
  5. 等等

一个快速测试某种类型是否可变的小技巧是使用内置的 id() 函数。

例如,使用在整数上,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

使用在列表上,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)
198

你需要明白,Python把所有的数据都当作对象来处理。其中一些对象,比如列表和字典,是可变的,这意味着你可以改变它们的内容,而不需要改变它们的身份。其他一些对象,比如整数、浮点数、字符串和元组,是不可变的,也就是说你不能改变它们的内容。

理解这一点的一个简单方法是看看对象的ID。

下面你会看到一个字符串,它是不可变的。你不能改变它的内容。如果你尝试去改变它,会出现一个TypeError错误。而且,如果我们给它赋予新的内容,实际上是创建了一个新的对象,而不是修改原来的内容。

>>> s = "abc"
>>> id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>> id(s)
4800100
>>> s += "uvw"
>>> id(s)
4800500

但是对于列表,你可以改变它的内容,而它的身份不会改变。

>>> i = [1,2,3]
>>> id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

如果想了解更多关于Python数据模型的内容,可以查看Python的官方文档:

243

什么?浮点数是不可变的?那我不能这样做吗?

x = 5.0
x += 7.0
print x # 12.0

这不是在“改变”x吗?

你同意字符串是不可变的,对吧?但你也可以做同样的事情。

s = 'foo'
s += 'bar'
print s # foobar

变量的值确实在变化,但它是通过改变变量所指向的内容来变化的。可变类型可以这样改变,同时也可以“就地”改变。

这里是它们之间的区别。

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

具体例子

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

撰写回答