Python中del list[i]与切片拼接删除列表元素的本质区别
两种列表删除指定索引元素实现的差异对比
删除列表中索引为i的元素,两种常见写法如下:
第一种(原地删除):
i = 3 l = [0,1,2,3,4,5] del l[i]
第二种(切片拼接):
i = 3 l = [0,1,2,3,4,5] l = l[:i] + l[i+1:]
两种写法执行后打印变量l得到的表面结果一致,但核心逻辑存在本质区别,部分场景下第二种写法会直接引发错误。
核心本质差异
- 对原对象的修改逻辑不同:
del l[i]是原地修改操作,直接在当前绑定的列表对象上删除对应索引的元素,操作全程不会生成新的列表对象,列表的内存地址始终不变。而l = l[:i] + l[i+1:]是新建对象操作:先通过切片分别取索引i前后的两段数据生成两个新列表,拼接后得到一个完全独立的新列表对象,最后只是把变量l重新绑定到这个新列表上,原本的旧列表不会被修改。 - 性能开销不同:
del操作仅需要将索引i之后的所有元素向前移动一位,额外内存开销极低,时间复杂度为O(n)(仅计算元素移动的成本)。切片拼接的写法需要复制除被删元素外的所有元素,额外占用一份和原列表大小接近的内存,数据量越大,和原地删除的性能差距越明显。
第二种写法不适用的常见场景
- 多变量引用同一列表的场景
如果有其他变量同时指向原列表,第二种写法只会修改当前l变量的指向,其他引用原列表的变量完全感知不到变化,会读到未删除元素的旧数据,直接引发逻辑错误。举个简单例子:
i = 3 l1 = [0,1,2,3,4,5] l2 = l1 # l2和l1指向同一个列表对象 l1 = l1[:i] + l1[i+1:] print(l1) # 输出 [0,1,2,4,5] print(l2) # 输出 [0,1,2,3,4,5],l2指向的原列表根本没被修改
如果用del l1[i]的写法,l1和l2会同时拿到删除后的结果,符合预期。
列表被其他容器/对象引用的场景
如果列表是字典的value、类实例的属性、其他列表的元素,你在局部作用域拿到列表的引用后用切片拼接重新赋值,根本修改不到容器里存储的原列表。比如你把列表存在user_info["tag_list"]里,函数内取tags = user_info["tag_list"]再做切片重赋值,user_info里存的列表不会有任何变化。处理自定义列表子类的场景
如果你用的是继承自list的自定义子类(比如加了额外业务方法、属性的特殊列表),切片拼接返回的是原生的普通list对象,会直接丢失子类的所有自定义属性和方法,后续调用子类专属方法会直接报错。而del走的是子类自定义的__delitem__逻辑,不会改变对象本身的类型。处理超大列表的场景
当列表元素量级达到十万、百万级时,切片拼接复制全量数据的操作会占用大量额外内存,耗时也会数倍于原地删除,极端情况下甚至会触发内存不足的问题。
内容的提问来源于stack exchange,提问作者Xin Miao
相关产品推荐
相关产品推荐

