Python中修改元组内列表报错但修改成功的问题解析
为什么
tup[1]+=[2]报错但元组里的列表还是被修改了? 这是个特别有意思的问题,刚好戳中了元组“不可变”的本质和+=操作的执行逻辑!咱们一步步拆解来看:
先搞懂元组的“不可变”到底是什么
元组的不可变,不是说它里面的元素绝对不能变,而是元组本身作为一个容器,它存储的是各个元素的引用地址——这些引用地址是不能修改的(比如你不能把元组里的列表换成另一个新列表)。但如果某个元素本身是可变对象(比如列表、字典),那这个对象的内容是可以被修改的,因为修改内容并不会改变它的引用地址。
拆解tup[1]+=[2]的执行步骤
当你执行tup[1]+=[2]时,这个操作其实分两步走:
- 第一步:原地修改列表
tup[1]会先拿到元组里的列表对象[1,2],然后+=对于列表来说,等价于调用extend方法——它会直接在原列表对象上添加元素,这一步已经把列表变成了[1,2,2],而且这个列表的引用地址完全没变。 - 第二步:尝试赋值回元组,触发报错
+=操作在完成原地修改后,会尝试把这个修改后的列表重新赋值给tup[1](也就是试图改变元组里的引用地址),但元组的规则不允许修改元素的引用,所以这一步直接抛出TypeError: 'tuple' object does not support item assignment。
所以你看到的现象就是:前面的原地修改已经生效了,但后面的赋值步骤失败报错,最终元组里的列表确实被改了,但代码因为最后一步失败而报错。
对比append为什么不报错
而tup[1].append(2)就简单多了:它只是直接调用列表的append方法,纯粹在原列表对象上添加元素,全程没有尝试修改元组里的任何引用地址,所以完全不会触发元组的不可变限制,自然也就不会报错。
用id验证一下(可选)
你可以打印列表的内存地址来确认这一点:
tup = tuple(['foo', [1,2], True]) print(id(tup[1])) # 输出某个内存地址,比如140703324527424 try: tup[1] += [2] except TypeError: pass print(id(tup[1])) # 和之前的地址完全一样,说明还是同一个列表对象
这下是不是就完全清楚啦?核心就是+=多了一步赋值操作,而元组不允许这一步,但前面的原地修改已经完成了~
内容的提问来源于stack exchange,提问作者Bilal Khan
相关产品推荐
相关产品推荐

