一句话结论
python
a = new_list # 换指针:a 指向新对象,原对象不变
a[:] = new_list # 换内容:原对象被就地修改,所有引用者同步看到变化
1. 问题场景
你写了一个函数,想在内部"替换"传进来的 list:
python
def compress(messages):
summary = summarize(messages)
messages = [summary] # ← 这行有效吗?
history = [msg1, msg2, msg3, ...]
compress(history)
print(history) # 还是 [msg1, msg2, msg3, ...] !没变!
为什么?因为 Python 函数参数是引用传递 ,但 = 赋值只改变局部变量的指向,不影响外部。
2. 内存模型图解
普通赋值 messages = new_list
css
调用前:
history ──────┐
▼
messages ───► [msg1, msg2, msg3] ← 同一个对象
赋值后:
history ──────► [msg1, msg2, msg3] ← 原对象,没人动它
messages ────► [summary] ← 新对象,局部变量换了指向
函数结束,messages 局部变量销毁,[summary] 被 GC 回收。history 纹丝不动。
切片赋值 messages[:] = new_list
bash
调用前:
history ──────┐
▼
messages ───► [msg1, msg2, msg3] ← 同一个对象
赋值后:
history ──────┐
▼
messages ───► [summary] ← 同一个对象,内容被替换了!
[:] 是对对象本身动手术------清空原内容、填入新内容,对象 id 不变。
3. 验证:id 不变 vs id 变了
python
a = [1, 2, 3]
print(id(a)) # 4398046208
# 普通赋值
a = [4, 5, 6]
print(id(a)) # 4398100032 ← id 变了!新对象
# 切片赋值
a = [1, 2, 3]
print(id(a)) # 4398046208
a[:] = [4, 5, 6]
print(id(a)) # 4398046208 ← id 没变!同一个对象
4. 切片赋值的几种形式
| 语法 | 含义 | 等价操作 |
|---|---|---|
a[:] = x |
替换全部内容 | a.clear(); a.extend(x) |
a[1:3] = x |
替换索引 1、2 的元素 | 删掉旧的,插入新的 |
a[::2] = x |
替换偶数位置 | 要求 len(x) 匹配 |
a[:0] = x |
在开头插入 | a = x + a 的就地版 |
a[len(a):] = x |
在末尾追加 | a.extend(x) |
python
a = [0, 1, 2, 3, 4]
a[1:3] = [10, 20, 30] # 可以长度不等!
print(a) # [0, 10, 20, 30, 3, 4]
5. 常见陷阱
陷阱 1:tuple 没有切片赋值
python
t = (1, 2, 3)
t[:] = (4, 5, 6) # TypeError: 'tuple' object does not support item assignment
tuple 是不可变的,只能 t = new_tuple。
陷阱 2:dict 没有切片
python
d = {"a": 1}
d[:] = {"b": 2} # TypeError
# dict 的就地替换用:
d.clear()
d.update(new_dict)
陷阱 3:步长切片赋值长度必须匹配
python
a = [0, 1, 2, 3, 4]
a[::2] = [10, 20, 30] # ✅ 3个位置(0,2,4)对应 3个值
a[::2] = [10, 20] # ❌ ValueError: attempt to assign sequence of size 2
# to extended slice of size 3
6. 一图总结
css
┌─────────────────────────────────────────────────┐
│ 普通赋值 a = x │
│ • 局部变量换指向 │
│ • 原对象不变 │
│ • 其他引用者看不到变化 │
│ • id(a) 变了 │
├─────────────────────────────────────────────────┤
│ 切片赋值 a[:] = x │
│ • 原对象被就地修改 │
│ • 所有引用者同步看到 │
│ • id(a) 不变 │
│ • 适用于需要"通过参数修改调用方数据"的场景 │
└─────────────────────────────────────────────────┘
记忆口诀
=换人,[:]换心。普通赋值换了一个人(新对象),切片赋值换了心(原对象内容变了,人还是那个人)。