Python基础指南之列表删除元素的六大常用方式详解

 更新时间:2026年06月08日 09:20:05   作者:星河耀银海  
Python列表提供了惊人的六种删除方式,是pop()、remove()、clear()、del语句、切片赋空和切片删除,本文会把每种删除方式的适用场景、返回值、性能和避坑技巧都逐一讲清,同时重点讨论边遍历边删除这个经典难题的解决方案

一、开篇:删除比增加复杂得多

上一篇文章我们学了增加元素的四种方式,今天进入更复杂的主题——删除元素。Python列表提供了惊人的六种删除方式:pop()remove()clear()del语句、切片赋空、切片删除。

为什么删除比增加复杂?因为增加只关心"往哪加、加什么",而删除需要考虑"按什么删、删哪个、删完要不要返回值、删完列表怎么变"。选错删除方式,轻则代码可读性下降,重则产生难以排查的bug——比如在遍历时删除列表元素导致漏删或越界。

这篇文章我会把每种删除方式的适用场景、返回值、性能和避坑技巧都逐一讲清,同时重点讨论"边遍历边删除"这个经典难题的解决方案。

二、pop()——弹出并返回元素

2.1 基本用法

pop() 是唯一能同时完成删除和获取的方法——它删除指定位置的元素并返回它。

fruits = ['苹果', '香蕉', '橘子', '葡萄', '西瓜']

# pop():删除并返回最后一个元素(默认)
last = fruits.pop()
print(last)    # 西瓜
print(fruits)  # ['苹果', '香蕉', '橘子', '葡萄']

# pop(index):删除并返回指定索引的元素
second = fruits.pop(1)
print(second)  # 香蕉
print(fruits)  # ['苹果', '橘子', '葡萄']

# pop(0):删除并返回第一个元素
first = fruits.pop(0)
print(first)   # 苹果
print(fruits)  # ['橘子', '葡萄']

# pop负索引
fruits = ['苹果', '香蕉', '橘子', '葡萄']
second_last = fruits.pop(-2)
print(second_last)  # 橘子
print(fruits)       # ['苹果', '香蕉', '葡萄']

2.2 pop() 的错误处理

# 对空列表pop会报IndexError
empty = []
try:
    empty.pop()
except IndexError as e:
    print(f'错误:{e}')  # pop from empty list

# 索引越界也会报错
fruits = ['苹果', '香蕉']
try:
    fruits.pop(5)
except IndexError as e:
    print(f'错误:{e}')  # pop index out of range

# 安全pop的写法
def safe_pop(lst, index=-1, default=None):
    """安全地弹出元素,出错时返回默认值"""
    if not lst:
        return default
    try:
        return lst.pop(index)
    except IndexError:
        return default

fruits = ['苹果', '香蕉']
print(safe_pop(fruits))       # 香蕉
print(safe_pop(fruits))       # 苹果
print(safe_pop(fruits))       # None(列表已空)
print(safe_pop([], 0, '空'))  # '空'

2.3 pop() 实现栈和队列

# pop() 天然适合实现栈(后进先出 LIFO)
stack = []
stack.append('页面1')
stack.append('页面2')
stack.append('页面3')

# 后退操作
current = stack.pop()  # 返回'页面3'
print(f'当前页面: {current}')  # 当前页面: 页面3
current = stack.pop()  # 返回'页面2'
print(f'返回后页面: {current}')  # 返回后页面: 页面2

# pop(0) 可实现简单队列(先进先出 FIFO)
# 但效率低(O(n)),大量数据用collections.deque
queue = []
queue.append('任务1')
queue.append('任务2')
queue.append('任务3')

task = queue.pop(0)  # 从头部取出
print(f'处理: {task}')  # 处理: 任务1

虽然 pop(0) 能实现队列,但它的时间复杂度是 O(n)(后面的元素都要向前移动)。生产环境中大量数据用 collections.dequepopleft(),那是 O(1) 的。

三、remove()——按值删除

3.1 基本用法

remove(x) 删除列表中第一个值为 x 的元素。它不返回任何值(返回None)。

nums = [1, 3, 5, 3, 7, 3, 9]

# 删除第一个值为3的元素
nums.remove(3)
print(nums)  # [1, 5, 3, 7, 3, 9](只删除第一个)

# 再删一次
nums.remove(3)
print(nums)  # [1, 5, 7, 3, 9]

# 删除不存在的值——报ValueError
try:
    nums.remove(100)
except ValueError as e:
    print(f'错误:{e}')  # list.remove(x): x not in list

3.2 remove() 的常见模式

# 模式一:安全删除(值可能不存在)
def safe_remove(lst, value):
    """安全删除,值不存在时不报错"""
    try:
        lst.remove(value)
        return True
    except ValueError:
        return False


# 模式二:删除所有匹配的值
nums = [1, 3, 5, 3, 7, 3, 9, 3]
# 方式一:循环remove(简单但有性能顾虑)
while 3 in nums:
    nums.remove(3)
print(nums)  # [1, 5, 7, 9]

# 方式二:列表推导式(推荐)
nums = [1, 3, 5, 3, 7, 3, 9, 3]
nums = [x for x in nums if x != 3]
print(nums)  # [1, 5, 7, 9]

# 模式三:删除满足条件的第一个元素
def remove_first_matching(lst, predicate):
    """删除第一个满足条件的元素"""
    for i, item in enumerate(lst):
        if predicate(item):
            return lst.pop(i)
    return None

scores = [85, 92, 78, 95, 88]
removed = remove_first_matching(scores, lambda x: x < 80)
print(f'删除了: {removed}')  # 删除了: 78
print(f'剩余: {scores}')     # 剩余: [85, 92, 95, 88]

3.3 remove() 的局限性

# remove() 只能按值删除,不能按条件删除
# 不支持的场景:

items = ['apple', 'Banana', 'cherry', 'APPLE']
# 想删除不区分大小写的'apple'
# items.remove('apple')  # 只删除小写的
# items.remove('APPLE')  # 还要再删一次

# 正确方式:用列表推导式
items = [x for x in items if x.lower() != 'apple']
print(items)  # ['Banana', 'cherry']

# remove() 也不能删除满足某种条件的元素
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 想删除所有偶数
# 不能写成 nums.remove(x % 2 == 0)  # 这没有意义
# 正确方式:
nums = [x for x in nums if x % 2 != 0]
print(nums)  # [1, 3, 5, 7, 9]

四、clear()——一键清空

4.1 基本用法

# clear() 清空列表中的所有元素
fruits = ['苹果', '香蕉', '橘子']
fruits.clear()
print(fruits)  # []
print(len(fruits))  # 0

# clear() 等价于 del lst[:]
# 也等价于 lst[:] = []
fruits = ['苹果', '香蕉', '橘子']
del fruits[:]
print(fruits)  # []

fruits = ['苹果', '香蕉', '橘子']
fruits[:] = []
print(fruits)  # []

# clear() vs 重新赋值为空列表
lst1 = [1, 2, 3]
lst2 = lst1  # lst2和lst1指向同一个列表
lst1 = []    # lst1指向了一个新的空列表
print(lst1)  # []
print(lst2)  # [1, 2, 3](lst2仍然指向原来的列表!)

lst1 = [1, 2, 3]
lst2 = lst1
lst1.clear()  # 清空原列表(所有引用它的变量都能看到变化)
print(lst1)  # []
print(lst2)  # [](lst2也看到列表被清空了)

lst.clear()lst = [] 的关键区别:clear() 清空原列表对象(所有引用都受影响),而 lst = [] 只是让变量指向新列表(其他引用不受影响)。

五、del 语句——按索引删除

5.1 基本用法

fruits = ['苹果', '香蕉', '橘子', '葡萄', '西瓜']

# del:删除指定索引的元素(无返回值)
del fruits[1]
print(fruits)  # ['苹果', '橘子', '葡萄', '西瓜']

# del支持负索引
del fruits[-1]
print(fruits)  # ['苹果', '橘子', '葡萄']

# del一次删除多个元素(按切片删除)
fruits = ['苹果', '香蕉', '橘子', '葡萄', '西瓜']
del fruits[1:3]
print(fruits)  # ['苹果', '葡萄', '西瓜']

# del删除带步长的切片
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
del nums[::2]  # 删除所有偶数索引位置的元素
print(nums)  # [1, 3, 5, 7, 9]

# del删除整个列表
del nums
# print(nums)  # NameError: name 'nums' is not defined

5.2 del vs pop() vs remove()

# 三者的对比
lst = [10, 20, 30, 40, 50]

# pop():返回被删除的元素,按索引删
lst1 = [10, 20, 30, 40, 50]
deleted = lst1.pop(2)
print(f'pop删除: {deleted}, 剩余: {lst1}')  # pop删除: 30, 剩余: [10, 20, 40, 50]

# remove():不返回值,按值删(删除第一个匹配的)
lst2 = [10, 20, 30, 40, 50]
lst2.remove(30)
print(f'remove删除后: {lst2}')  # remove删除后: [10, 20, 40, 50]

# del:不返回值,按索引删(更灵活但无返回值)
lst3 = [10, 20, 30, 40, 50]
del lst3[2]
print(f'del删除后: {lst3}')  # del删除后: [10, 20, 40, 50]

# 选择指南:
# - 需要被删除的值 → pop()
# - 知道索引但不需要返回值 → del
# - 知道值但不知道位置 → remove()
# - 清空全部 → clear()
# - 删除一段 → del和切片

六、切片方式删除——最灵活

6.1 切片赋空(lst[a:b] = [])

nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 删除一段
nums[3:7] = []
print(nums)  # [0, 1, 2, 7, 8, 9]

# 删除前N个
nums[:3] = []
print(nums)  # [7, 8, 9]

# 删除后N个
nums[-2:] = []
print(nums)  # [7]

# 删除偶数索引(等价于del nums[::2])
nums = [0, 1, 2, 3, 4, 5, 6, 7]
nums[::2] = []  # 注意!步长≠1时,要求赋的值和切片元素数相等
print(nums)  # [1, 3, 5, 7]
# 此时[::2]切片结果有4个元素,赋空列表[](0个元素),不匹配→报错?
# 实际上:步长≠1时,赋值的元素数量必须和切片匹配
# 但赋空列表是个例外...等等,让我实测:

# 实际上在Python中:
nums = [0, 1, 2, 3, 4, 5, 6, 7]
# nums[::2] = []  # ValueError: attempt to assign sequence of size 0 to extended slice of size 4
# 所以步长≠1时,必须用del
del nums[::2]
print(nums)  # [1, 3, 5, 7]

6.2 切片删除的应用

# 删除前N个和后M个之外的所有元素(保留中间)
def keep_middle(lst, remove_front, remove_back):
    lst[:remove_front] = []
    lst[-remove_back:] = []
    return lst

data = list(range(1, 21))  # 1到20
result = keep_middle(data, 5, 5)
print(result)  # [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

# 清空列表但保持同一个对象
lst1 = [1, 2, 3]
lst2 = lst1
lst1[:] = []  # 清空内容但不改变对象身份
print(lst1)  # []
print(lst2)  # [](lst2也看到了变化)

# = vs [:] = [] vs clear()
a = [1, 2, 3]
b = a

# a = []:a指向新列表,b不变
# a[:] = []:原地清空,b也变
# a.clear():原地清空,b也变

七、遍历时安全删除——经典难题的完整解决方案

7.1 为什么遍历时删除会出问题

# 这是Python中最经典的陷阱之一
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 错误示范:用for循环遍历时删除
# for i in range(len(nums)):
#     if nums[i] % 2 == 0:
#         del nums[i]  # 删除后索引会移位!后续元素会跳过或越界

# 具体说明:
items = ['a', 'b', 'c', 'd']
# 想删除'b'和'c'
# for i in range(len(items)):
#     if items[i] in ('b', 'c'):
#         del items[i]
# i=0: items[0]='a',不删
# i=1: items[1]='b',删除 → items变为['a','c','d']
# i=2: items[2]='d'('c'被跳过了!因为列表移位)
# 结果:'c'没有被检查到!

7.2 解决方案汇总

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
target = [1, 2, 3, 4, 5, 6, 7, 8, 9]

# ✅ 方案一:创建新列表(最推荐,最清晰)
result1 = [x for x in target if x % 2 != 0]
print(f'方案一(推导式): {result1}')

# ✅ 方案二:从后往前遍历(避免索引移位的影响)
target = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in range(len(target) - 1, -1, -1):
    if target[i] % 2 == 0:
        del target[i]
print(f'方案二(逆序遍历): {target}')

# ✅ 方案三:用while + 条件判断
target = [1, 2, 3, 4, 5, 6, 7, 8, 9]
i = 0
while i < len(target):
    if target[i] % 2 == 0:
        del target[i]  # 注意:删除后i不增加
    else:
        i += 1
print(f'方案三(while循环): {target}')

# ✅ 方案四:先收集要删除的,再统一删除
target = [1, 2, 3, 4, 5, 6, 7, 8, 9]
to_delete = [i for i, x in enumerate(target) if x % 2 == 0]
for i in reversed(to_delete):
    del target[i]
print(f'方案四(先收集后删除): {target}')

# ✅ 方案五:filter函数
target = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result5 = list(filter(lambda x: x % 2 != 0, target))
print(f'方案五(filter): {result5}')

# ✅ 方案六:切片赋值(原地修改)
target = [1, 2, 3, 4, 5, 6, 7, 8, 9]
target[:] = [x for x in target if x % 2 != 0]
print(f'方案六(切片赋值): {target}')

7.3 方案选择指南

# 日常开发选择优先级:

# 首选:列表推导式创建新列表(最Pythonic)
# 适用于:大多数场景,代码最清晰
result = [x for x in original if condition(x)]

# 如果必须原地修改(其他变量也引用了这个列表):
# 用切片赋值
original[:] = [x for x in original if condition(x)]

# 如果列表非常大且条件很少触发删除:
# 用逆序遍历
for i in range(len(original) - 1, -1, -1):
    if condition(original[i]):
        del original[i]

# 如果列表中包含需要特殊处理的删除:
# 用while循环
i = 0
while i < len(original):
    if condition(original[i]):
        del original[i]
    else:
        i += 1

八、实战:用删除操作清理数据

8.1 清理列表中的无效数据

def clean_data(items):
    """清理列表中的无效数据(None、空字符串、0等)"""
    return [x for x in items if x is not None and x != '' and x != 0]

raw_data = ['hello', None, '', 'world', 0, 'python', None, '', 42]
cleaned = clean_data(raw_data)
print(cleaned)  # ['hello', 'world', 'python', 42]


def remove_outliers(scores, lower_bound, upper_bound):
    """移除超出范围的分数(离群值)"""
    return [s for s in scores if lower_bound <= s <= upper_bound]

scores = [85, 200, 92, -10, 78, 95, 150, 88]
valid_scores = remove_outliers(scores, 0, 100)
print(valid_scores)  # [85, 92, 78, 95, 88]


def deduplicate_keep_order(items):
    """去重且保持原始顺序"""
    seen = set()
    # 不能直接修改正在遍历的列表,但可以构建新列表
    return [x for x in items if not (x in seen or seen.add(x))]

items = [1, 3, 2, 1, 5, 3, 2, 4, 1]
print(deduplicate_keep_order(items))  # [1, 3, 2, 5, 4]

8.2 栈式操作实战——撤销功能

class UndoManager:
    """用pop()实现撤销功能"""
    def __init__(self):
        self._history = []
        self._redo_stack = []

    def do(self, action):
        """执行一个操作"""
        self._history.append(action)
        self._redo_stack.clear()  # 新操作后不能重做
        print(f'执行: {action}')

    def undo(self):
        """撤销上一个操作"""
        if not self._history:
            print('没有可撤销的操作')
            return
        action = self._history.pop()
        self._redo_stack.append(action)
        print(f'撤销: {action}')

    def redo(self):
        """重做上一个撤销的操作"""
        if not self._redo_stack:
            print('没有可重做的操作')
            return
        action = self._redo_stack.pop()
        self._history.append(action)
        print(f'重做: {action}')


manager = UndoManager()
manager.do('输入"hello"')      # 执行: 输入"hello"
manager.do('输入" world"')     # 执行: 输入" world"
manager.do('删除" world"')     # 执行: 删除" world"
manager.undo()                # 撤销: 删除" world"
manager.undo()                # 撤销: 输入" world"
manager.redo()                # 重做: 输入" world"

8.3 处理嵌套列表中的删除

def deep_remove(lst, target):
    """递归删除嵌套列表中的所有匹配元素"""
    result = []
    for item in lst:
        if isinstance(item, list):
            # 递归处理嵌套列表
            cleaned = deep_remove(item, target)
            if cleaned:  # 跳过空列表
                result.append(cleaned)
        elif item != target:
            result.append(item)
    return result


nested = [1, 2, [3, 2, [4, 2, 5], 2], 6, 2, [7, 2]]
cleaned = deep_remove(nested, 2)
print(cleaned)  # [1, [3, [4, 5]], 6, [7]]

九、性能与陷阱

9.1 删除操作的性能对比

import time

n = 10000

# pop()——末尾弹出 O(1)
lst = list(range(n))
start = time.perf_counter()
while lst:
    lst.pop()
t_pop_end = time.perf_counter() - start
print(f'pop()末尾: {t_pop_end:.4f}秒')

# pop(0)——头部弹出 O(n)
lst = list(range(n))
start = time.perf_counter()
while lst:
    lst.pop(0)
t_pop_front = time.perf_counter() - start
print(f'pop(0)头部: {t_pop_front:.4f}秒')
print(f'头部比末尾慢 {t_pop_front / t_pop_end:.0f} 倍')

# remove()——按值删除 O(n)
lst = list(range(n))
start = time.perf_counter()
for i in range(n):
    lst.remove(i)
t_remove = time.perf_counter() - start
print(f'remove(): {t_remove:.4f}秒')

# 列表推导式——O(n)
lst = list(range(n))
start = time.perf_counter()
lst = [x for x in lst if x < n // 2]
t_comp = time.perf_counter() - start
print(f'推导式: {t_comp:.4f}秒')

9.2 常见陷阱

# 陷阱一:remove()只删除第一个匹配
lst = [1, 1, 1, 1, 1]
lst.remove(1)
print(lst)  # [1, 1, 1, 1](只删了一个!)

# 陷阱二:pop()不可对空列表操作
# [].pop()  # IndexError

# 陷阱三:del删除变量和del删除元素的混淆
# del list_name  # 删除整个变量
# del list_name[index]  # 删除元素

# 陷阱四:在循环中修改列表
lst = [1, 2, 3, 4, 5]
for x in lst:  # 遍历的是原始列表!
    if x % 2 == 0:
        lst.remove(x)
print(lst)  # [1, 3, 5](看起来正确,但内部跳过了一些元素)
# 实际过程:
# 迭代1: x=1,不删
# 迭代2: x=2,删除 → lst变为[1,3,4,5]
# 迭代3: x=4  ← 跳过了3!
# 迭代4: x=5,不删

# 对于[2, 4, 1, 3, 5]这种排列,结果就错了:
lst = [2, 4, 1, 3, 5]
for x in lst:
    if x % 2 == 0:
        lst.remove(x)
print(lst)  # [1, 3, 5] —— 这个例子恰好对了
# 但换成 [2, 2, 4, 1, 3, 5] 看看:
lst = [2, 2, 4, 1, 3, 5]
for x in lst:
    if x % 2 == 0:
        lst.remove(x)
print(lst)  # [2, 1, 3, 5] —— 少删了一个2!

# 教训:永远不要在用for循环遍历列表时删除其中的元素

十、本篇小结

Python列表六大删除方式速查:

方式按什么删有无返回值时间复杂度最适用场景
pop()索引(默认最后)O(1)末尾/O(n)头部需要拿到被删元素
remove()值(第一个匹配)O(n)知道值不知道位置
clear()全部O(1)一键清空
del lst[i]索引O(n)精确索引删除
del lst[a:b]切片范围O(n)删除一段
lst[:]=[...]替换全部O(n)原地过滤

核心要诀:

  • 需要返回值 → pop()
  • 知道值不知道位置 → remove()
  • 精确按位置删 → del lst[i]
  • 清空全部 → clear()
  • 遍历时删除 → 用列表推导式创建新列表,逆序遍历,或while循环

删除是列表操作中最容易踩坑的领域——特别是"边遍历边删除"。记住黄金法则:不确定的时候,就用列表推导式创建新列表。下一篇我们进入列表的查找、排序与反转方法。

以上就是Python基础指南之列表删除元素的六大常用方式详解的详细内容,更多关于Python列表删除元素的资料请关注脚本之家其它相关文章!

相关文章

  • python根据时间生成mongodb的ObjectId的方法

    python根据时间生成mongodb的ObjectId的方法

    这篇文章主要介绍了python根据时间生成mongodb的ObjectId的方法,涉及Python操作mongodb数据库的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • Tensorflow与Keras自适应使用显存方式

    Tensorflow与Keras自适应使用显存方式

    这篇文章主要介绍了Tensorflow与Keras自适应使用显存方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • Python与数据库交互:入门指南

    Python与数据库交互:入门指南

    这篇文章主要介绍了Python与数据库交互:入门指南的相关资料,需要的朋友可以参考下
    2023-11-11
  • Python3基础之list列表实例解析

    Python3基础之list列表实例解析

    这篇文章主要介绍了Python3的list列表用法,这是Python3数据类型中非常常见的应用,需要的朋友可以参考下
    2014-08-08
  • python包合集shutil示例代码详解

    python包合集shutil示例代码详解

    shutil是 python 中的高级文件操作模块,与os模块形成互补的关系,os主要提供了文件或文件夹的新建、删除、查看等方法,还提供了对文件以及目录的路径操作,这篇文章主要介绍了python包合集-shutil,需要的朋友可以参考下
    2022-08-08
  • 在VS Code中切换和设置Python解释器的完整指南

    在VS Code中切换和设置Python解释器的完整指南

    这篇文章主要为大家详细介绍了在VS Code中切换Python环境的详细方法,支持Windows/macOS/Linux系统及虚拟环境/Conda环境,有需要的小伙伴可以了解下
    2026-03-03
  • 解决Python运行文件出现out of memory框的问题

    解决Python运行文件出现out of memory框的问题

    今天小编就为大家分享一篇解决Python运行文件出现out of memory框的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-12-12
  • Python爬虫实现简单的爬取有道翻译功能示例

    Python爬虫实现简单的爬取有道翻译功能示例

    这篇文章主要介绍了Python爬虫实现简单的爬取有道翻译功能,结合实例形式分析了Python基于urllib库实现的爬虫爬取有道翻译相关定义与数据交互功能操作技巧,需要的朋友可以参考下
    2018-07-07
  • python图形工具turtle绘制国际象棋棋盘

    python图形工具turtle绘制国际象棋棋盘

    这篇文章主要为大家详细介绍了python图形工具turtle绘制国际象棋棋盘,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • Pytorch自定义CNN网络实现猫狗分类详解过程

    Pytorch自定义CNN网络实现猫狗分类详解过程

    PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。它不仅能够实现强大的GPU加速,同时还支持动态神经网络。本文将介绍PyTorch自定义CNN网络实现猫狗分类,感兴趣的可以学习一下
    2022-12-12

最新评论