Python中的数据对象不可变性指的是对象创建之后,其内部存储的数据内容无法被修改,任何看似修改的操作都会生成一个新的对象实例。这种特性在很多场景下能避免意外的数据篡改,提升代码的可靠性。

常见的不可变数据对象类型
Python内置的不可变数据对象主要包括数值类型(int、float、bool)、字符串str、元组tuple、冻结集合frozenset,以及字节串bytes。这些对象在创建之后,都无法直接修改其内部的值。
不可变对象与可变对象的对比
为了更清晰地理解不可变对象的特点,我们可以将其和可变对象做对比,具体差异如下:
| 对比维度 | 不可变对象 | 可变对象 |
|---|---|---|
| 创建后内容是否可改 | 不可修改,修改会生成新对象 | 可以直接修改内部内容 |
| 常见类型 | int、str、tuple、frozenset | list、dict、set、bytearray |
| 哈希支持 | 可以作为字典的键、集合的元素 | 不能作为字典的键、集合的元素 |
不可变性的底层实现逻辑
1. 对象内部属性限制
Python的不可变对象在定义时,会将存储数据的核心属性设置为不可写状态。以整数int类型为例,其底层存储数值的属性没有暴露修改的接口,开发者无法直接对该属性赋值。
我们可以通过简单的代码验证这个特性:
# 尝试修改整数对象的属性会报错
num = 10
# int对象没有__dict__属性,无法动态添加或修改属性
try:
num.value = 20
except AttributeError as e:
print("修改整数属性失败:", e)
2. 操作符重载的限制
不可变对象的修改相关操作符都被重载为返回新对象的逻辑,而不是修改原有对象。比如字符串的拼接操作,不会修改原来的字符串,而是生成一个新的字符串对象。
下面的代码可以体现这个特点:
s1 = "hello"
s2 = s1 + " world"
# s1的值没有变化,s2是新生成的对象
print("s1的值:", s1) # 输出 hello
print("s2的值:", s2) # 输出 hello world
print("s1和s2是否是同一个对象:", s1 is s2) # 输出 False
3. 元组等容器的不可变性实现
元组tuple的不可变性是指其存储的元素引用不可修改,但是元素本身如果是可变对象,那么该可变对象的内容还是可以修改的。元组的底层实现中,存储元素引用的数组被设置为不可变,无法替换其中的引用。
相关示例如下:
t = (1, [2, 3])
# 无法修改元组内的元素引用,下面这行代码会报错
try:
t[0] = 2
except TypeError as e:
print("修改元组元素引用失败:", e)
# 但是元组内的可变对象内容可以修改
t[1].append(4)
print("修改后的元组:", t) # 输出 (1, [2, 3, 4])
4. 小整数池与字符串驻留机制
Python为了提升不可变对象的复用效率,还实现了小整数池和字符串驻留机制。小整数池会预先创建一定范围的整数对象,字符串驻留会让相同内容的字符串指向同一个对象,这从侧面强化了不可变对象的特性,因为相同值的对象本身就是同一个实例,修改任何一个都会影响所有引用,所以干脆不允许修改。
验证代码如下:
# 小整数池验证
a = 10
b = 10
print("a和b是否是同一个对象:", a is b) # 输出 True
# 字符串驻留验证
s1 = "test"
s2 = "test"
print("s1和s2是否是同一个对象:", s1 is s2) # 输出 True
自定义不可变数据对象
除了内置的不可变对象,我们还可以通过重写类的相关方法,自定义不可变的数据对象。核心是要禁止对象的属性修改,通常可以重写__setattr__方法,在属性赋值时抛出异常。
自定义不可变对象的示例如下:
class ImmutablePoint:
def __init__(self, x, y):
# 调用父类的__setattr__方法初始化属性,绕过自定义的__setattr__
super().__setattr__("x", x)
super().__setattr__("y", y)
def __setattr__(self, name, value):
# 禁止修改任何属性
raise AttributeError("不可变对象,无法修改属性")
def __repr__(self):
return f"ImmutablePoint(x={self.x}, y={self.y})"
# 测试自定义不可变对象
p1 = ImmutablePoint(1, 2)
print("p1:", p1) # 输出 ImmutablePoint(x=1, y=2)
# 尝试修改属性会报错
try:
p1.x = 3
except AttributeError as e:
print("修改自定义不可变对象属性失败:", e)
通过上述实现方式,我们就可以创建出符合自己需求的不可变数据对象,在需要保证数据不被意外修改的场景下使用。