Python 修改不可变变量的值:没有原地修改,只能重新赋值

先记住核心结论: 不可变对象(int/float/str/tuple/bool/frozenset不能在原有内存地址上修改内部数据,想要改变变量存储的值,只能让变量名指向一个全新的对象。

一、什么叫重新赋值

旧对象保留在内存中,变量不再引用它,转而绑定新的值,内存地址会发生改变。

1. 整数 int 示例

python

运行

num = 10
print(id(num))

# 重新赋值,不是修改原来的10
num = 20
print(id(num))  # 地址改变,生成新对象

2. 字符串 str 示例

字符串没有任何原地修改方法,拼接、替换都会返回新字符串:

python

运行

s = "hello"
new_s = s + " world"
# 原字符串 s 没变,只是用新变量接收新字符串
# 如果要覆盖原变量名:
s = s.replace("hello", "hi")

3. 元组 tuple 示例

元组不能通过索引修改元素,只能拼接、切片生成新元组再赋值:

python

运行

t = (1, 2, 3)
# t[0] = 100  直接报错,无法原地修改

# 正确方式:生成新元组重新赋值
t = t + (4,)
print(t)  # (1, 2, 3, 4)

4. 浮点数、布尔

python

运行

pi = 3.14
pi = 3.14159

flag = True
flag = False

5. 不可变集合 frozenset

只能创建新集合再赋值:

python

运行

fs = frozenset({1,2})
# fs.add(3) 报错
fs = frozenset({1,2,3})

二、常见误区:不是原地修改,只是换了个新对象

python

运行

a = "abc"
a += "d"

很多人以为这是修改字符串,本质:

  1. 先创建新字符串 abcd
  2. 变量 a 放弃原来 abc 的引用,指向新字符串
  3. 旧的 abc 字符串不会被改动,等待垃圾回收

三、如果一定要 “原地可变” 怎么办?两种方案

方案 1:用可变类型包裹不可变数据

把数字、字符串放到列表里,通过列表原地修改:

python

运行

num = [10]
num[0] = 20  # 列表可变,原地修改包裹的数据

方案 2:使用 nonlocal / global 修改变量作用域绑定

不是修改对象本身,而是修改变量名绑定的值:

python

运行

count = 0
def add():
    global count
    count += 1  # 本质:count = count + 1,重新绑定新int对象

四、易错补充:元组内含可变元素可以改内部数据

python

运行

t = (1, [2, 3])
t[1].append(4)

这里并没有修改元组本身,只是修改了元组内部的列表(可变对象),元组里存储的列表内存地址没有变,不属于修改不可变类型。

总结

  1. 不可变类型无法原地修改,只能通过重新赋值让变量指向新对象;
  2. 字符串、元组的拼接、替换、切片操作,都会返回全新对象;
  3. 想要实现原地修改效果,可以用列表等可变容器包裹不可变数据;
  4. globalnonlocal 只是修改变量绑定关系,不是修改原始不可变对象。

需要分别整理字符串、元组常用生成新对象的方法清单吗?