Python 可变性、身份与拷贝
用对象身份、相等与可变性解释共享副作用;浅拷贝与深拷贝的边界与选用。
#type / concept
#status / growing
#tech / dev
#resource / python
#tech / lang / python
[!info] 关联笔记
Python 可变性、身份与拷贝
这个概念为什么出现
“我只改了局部变量,为什么外面的订单也变了?”——根因几乎总是:多个名字绑定同一可变对象。本篇把 is/==、可变/不可变、浅/深拷贝钉成可检查模型。
[!abstract] 一句话理解
is比身份、==比值;可变对象的原地修改对所有别名可见;浅拷贝只复制容器壳,深拷贝递归复制。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:克隆购物车草稿做 A/B 运费试算
结算要复制购物车:改副本行项目不应污染原购物车;若只浅拷贝,内层 list 仍共享。
# cart_copy_demo.py
# 业务意图:对比别名、浅拷贝、深拷贝对购物车的影响。
# 教学点:
# - is vs ==;
# - copy.copy 浅拷贝;
# - copy.deepcopy 深拷贝。
import copy
def main() -> None:
cart = {"user": "u1", "items": ["SKU-1", "SKU-2"]}
alias = cart
shallow = copy.copy(cart)
deep = copy.deepcopy(cart)
alias["user"] = "u-alias"
shallow["items"].append("SKU-X") # 影响原 cart.items
deep["items"].append("SKU-DEEP")
print("same cart is alias?", cart is alias)
print("cart == shallow?", cart == shallow)
print("cart items:", cart["items"])
print("shallow items:", shallow["items"])
print("deep items:", deep["items"])
print("cart user:", cart["user"])
if __name__ == "__main__":
main()
建议运行:
python cart_copy_demo.py
期望输出:
same cart is alias? True
cart == shallow? False
cart items: ['SKU-1', 'SKU-2', 'SKU-X']
shallow items: ['SKU-1', 'SKU-2', 'SKU-X']
deep items: ['SKU-1', 'SKU-2', 'SKU-DEEP']
cart user: u-alias
结合场景再看三个关注点
- 别名改
user直接改原对象。 - 浅拷贝复制了 dict 壳,但
itemslist 仍共享。 - 深拷贝后的追加不影响原购物车。
核心概念与准确模型
身份与相等
| 运算 | 含义 |
|---|---|
a is b | 同一对象 |
a == b | 值相等(可重载) |
a is None | None 哨兵推荐写法 |
可变性
- 不可变:
int/str/tuple/frozenset/...(对象值不改) - 可变:
list/dict/set/自定义对象字段...
拷贝
- 浅:
copy.copy/ 切片 /dict.copy/list(...) - 深:
copy.deepcopy
边界情况与反直觉行为
- 小整数驻留使
is对字面量偶发 True——勿当业务逻辑。 - deepcopy 遇循环引用可处理,但成本高。
- 自定义对象默认浅拷贝共享子对象。
常见误区
[!warning] 常见误区:
==与is混用 错误理解:一样。
正确模型:值 vs 身份;None用is。
工程实践
- 函数参数文档写明是否原地修改。
- 需要隔离副作用时明确 copy 策略。
- 性能敏感路径避免无脑 deepcopy。
本节总结
共享可变状态是 Python 最常见心智坑。先画“几个名字几个对象”,再决定要不要拷贝。
自测题
- 为什么浅拷贝后改
items会影响原 cart? - 什么场景选深拷贝?
参考答案
- 浅拷贝只复制外层 dict,内层 list 仍是同一对象。
- 嵌套可变结构需要完全隔离修改时(如试算草稿)。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| copy — Shallow and deep copy | 文档 | 拷贝 API |
| Naming and binding | 规范 | 绑定 |