因为list是可变对象,函数内调用.append()等方法直接修改原内存地址数据;而str、int等不可变对象在函数内赋值操作会创建新对象,不影响外部变量。

函数参数传进去的是 list,为什么原列表被改了?
因为 list 是可变对象,函数内部调用 .append()、.pop()、.clear() 等方法时,操作的是原始内存地址上的数据,不会新建对象。变量名只是标签,函数形参拿到的和调用方实参是同一个对象引用。
常见错误现象:
- 写了个工具函数处理配置列表,结果主逻辑里的配置被悄悄清空了
- 递归中传入
path = [],每层都.append(),最后所有路径都混在一起
解决建议:
- 若函数本意是“读取并计算”,但意外调用了修改方法,应加注释或封装为只读视图(如用
tuple(path)临时转不可变) - 若需保留原始状态,进函数第一行就做浅拷贝:
local_list = input_list.copy()或local_list = input_list[:] - 避免在函数签名里默认使用可变对象作参数(如
def func(items=[])),这会导致多次调用间状态残留
str 和 int 当参数传进去,为什么外部变量没变?
因为 str、int、tuple 是不可变对象。函数内任何“赋值类操作”(+=、+、.replace())都会创建新对象,形参变量会绑定到新对象,但不会影响调用方变量所指向的旧对象。
使用场景:
- 适合做字典键:
{'name': 'alice', (1, 2): 'point'}——tuple可哈希,list不行 - 适合做函数默认参数:
def greet(name, prefix='Hello'),不用担心字符串被意外覆盖
注意点:
-
s += 'x'看似就地修改,实际等价于s = s + 'x',生成新str对象 - 小整数(-5 到 256)有缓存,
a = 10; b = 10; a is b为True,但这只是优化,不改变不可变语义
为什么 dict 不能当字典的 key,而 tuple 可以?
因为字典的 key 必须可哈希(hashable),而可哈希的前提是对象的值不可变——否则插入后哈希值变化,会导致后续 dict[key] 查不到。
dict、list、set 是可变对象,hash({}) 会直接报 TypeError: unhashable type;tuple 本身不可变,所以能用,但要注意:如果 tuple 里嵌套了 list,例如 (1, [2, 3]),它依然不可哈希——hash() 会递归检查每个元素。
实操建议:
- 需要把一组值当 key 用,优先考虑
tuple,确保里面全是不可变类型 - 想让自定义类实例可哈希,必须实现
__hash__且保证其依赖的属性不可变(通常配合__slots__和只读属性) - 临时需要可哈希版本,可用
json.dumps(data, sort_keys=True)转字符串(但注意浮点精度、顺序等陷阱)
copy() 和 deepcopy() 哪个该用?
取决于你是否要切断嵌套引用。对纯一维 list 或 dict,.copy()(或 copy.copy())够用;一旦里面含 list、dict、自定义对象等,就得用 copy.deepcopy()。
容易踩的坑:
-
a = [[1, 2], [3]]; b = a.copy(); b[0].append(99)→a也变了,因为外层 list 是新的,但内层[1, 2]还是同一个对象 -
deepcopy()性能开销大,尤其数据深、体积大时;循环引用对象(如父子节点互指)不用它会无限递归 - 某些对象(如文件句柄、线程锁)无法被
deepcopy,会抛TypeError
替代思路:
- 用
dataclasses.replace()处理简单数据类 - 构造新容器而非复制:如
new_dict = {k: v for k, v in old_dict.items()}(浅层) - 明确设计为不可变结构:用
types.MappingProxyType包装dict,获得只读视图
assert isinstance(param, tuple), "expect immutable" 或显式拷贝,把隐式共享变成显式决策。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











