在Python中,对象根据其值是否可以被修改被分为两大类:可变对象和不可变对象。这两类对象在处理方式、内存占用以及在某些操作中的行为上有所不同。
1、不可变对象
不可变对象一旦创建,其内容就不能被修改。不可变对象在Python中很常见,比如整数(int)、浮点数(float)、复数(complex)、字符串(str)和元组(tuple)。
因为 Python 中的变量存放的是 对象引用,所以对不可变对象而言,尽管对象本身不可改变,但 变量对对象的引用或指向关系仍是可变的。具体而言,指向原不可变对象的变量被改变为指向新对象时,Python 会开辟一块新的内存区域,并令变量指向这个新内存 (存放新对象引用),因此 变量对对象的引用或指向关系是灵活的、可变的。
- 特性:
- 不可变对象的内容在创建后不能被修改。
- 如果尝试修改一个不可变对象,Python 会创建一个新的对象,该对象包含了你所做的修改,并将新的引用指向这个新创建的对象。
- 由于内容不可变,不可变对象在多个地方被引用时更为安全,因为不用担心其值会在某个地方被意外修改。
# 假设我们有一个不可变的字符串对象
s = "hello"
# 当我们尝试连接(修改)这个字符串时
s = s + " world"
# Python 并没有修改原始的 "hello" 字符串
# 而是创建了一个新的字符串对象 "hello world",并将 s 的引用指向了它
# 原始的 "hello" 字符串仍然存在于内存中(至少在一个引用周期内),但 s 不再指向它.
# 原不可变对象 hello内存中的值并未改变,Python 创建了新不可变对象 hello world,并令变量 s 重新指向新不可变对象 hello world(保存对新对象 75 的引用)
敲黑板:
不可变对象自身并未改变,而是创建了新不可变对象,改变了变量的对象引用
2、可变对象
可变对象在创建后其内容可以被修改。(变量所指向对象的内存地址处的值可改变)在Python中,==列表(list)、字典(dict)和集合(set)==都是可变对象的例子。
因此指向可变对象的变量若发生改变,则该可变对象亦随之改变,即发生 原地 (in-place) 修改。另一方面, 当可变对象相应内存中的值变化时,变量的对可变对象引用仍保持不变,即变量仍指向原可变对象。
- 特性:
- 可变对象的内容在创建后可以被修改。
- 由于内容可变,当可变对象在多个地方被引用时,需要特别注意其值的修改可能会影响到所有引用它的地方。
示例
# 不可变对象示例
s = "hello"
# 尝试修改字符串(实际上创建了一个新字符串)
s = s + " world" # 现在s指向了一个新的字符串对象"hello world"
# 可变对象示例
lst = [1, 2, 3]
id(lst)
1841032547080
# 修改列表的内容
lst.append(4) # lst现在包含了[1, 2, 3, 4]
id(lst)
1841032547080
敲黑板:
可变对象随着变量的改变而改变,但变量对可变对象的引用关系仍保持不变,即变量仍指向原可变对象。
3、性能考虑
- 不可变对象:由于内容不可变,它们可以被安全地共享和缓存,因为不需要担心它们的内容会被修改。Python的字符串驻留(string interning)机制就是基于这一点的。
- 可变对象:由于内容可变,它们可能需要更复杂的内存管理策略。例如,当可变对象的大小发生变化时,可能需要重新分配内存。此外,由于可变对象可以在多个地方被引用,因此对它们的修改可能会影响到程序的多个部分,这需要额外的注意。
4、总结
理解Python中的可变对象和不可变对象对于编写高效、安全的代码至关重要。它们之间的主要区别在于内容是否可以被修改,以及这种修改如何影响程序的执行和内存使用。
题外话:
分清楚了可变对象和不可变对象,但是对象是怎么传递和引用的?