遍历的list 的时候 删除元素 注意事项

遍历的list 的时候 直接删除元素 注意事项

概要

记得几年前 那个时候刚刚学习Python 这种语言,就了解这门比较简单易学,容易上手,这一点毋庸置疑, 但是随着对于Python 语言的理解,觉得这本语言并不简单,也有很多时候 不注意容易写出来的bug ,今天我就来分享一下 ,那些年 我曾经写过的bug ,希望可以帮助 新同学 及早地避免这样的bug .

问题引入举例

举一个很常见的例子,在列表中 过滤元素, 有一个列表我不希望保留某个元素的值,比如删除 元素为3的值

# 有一个list, list 中存放int类型的数据 , 删除 元素为 3 的值,返回一个list
from typing import List


def delete_arr(arr: List[int]) -> List[int]:
    for num in arr:
        # print(f"num:{num}")
        if num == 3:
            arr.remove(num)
    return arr

 
  
if __name__ == '__main__':
    arr = [1, 2, 3, 4, 5, 6, 7]
    arr2 = delete_arr(arr)
    print(arr2)  # [1, 2, 4, 5, 6, 7]

看到这样的结果 也没有问题, 成功的删除了对应为3的元素

这段代码 是有潜在的问题的, 假如 我现在 arr 为 arr = [1, 2, 3, 3, 4, 5, 3, 6] ,在跑一次 上面的代码 结果如下:

[1, 2, 4, 5, 3, 6]

发现 3这个元素 没有完全删除,这是为什么呢?

要想解决这个问题, 我们对Python 语言 for 循环这个语法更加有深刻的理解。

for item in iterable for 循环的对象 是一个 可迭代对象, 每次其实调用的是 可迭代对象的 __next__ 产生的下一个元素.

迭代器

我们来实现 一个简单的迭代器,在接口的层次我们 实现两个魔术方法, __iter____next__这两个方法。

__iter__ 方法用来返回一个可迭代对象, next 返回下一个迭代的元素,如果迭代到最后的一个元素 抛出一个异常StopIteration 迭代就结束了。

class MyIterator:
    """
    实现一个 迭代器
    """
    def __init__(self, max_size=5, count=0):
        self.count = count
        self.max_size = max_size

    def __iter__(self):
        return self

    def __next__(self):
        self.count += 1
        if self.count > self.max_size:
            raise StopIteration('stop')
        return self.count

>>> collection = MyIterator()
>>> 
>>> iterator = iter(collection)
>>> next(iterator)
1
>>> next(iterator)
2
>>> next(iterator)
3
>>> next(iterator)
4
>>> next(iterator)
5
>>> next(iterator)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 18, in __next__
StopIteration: stop

collection = MyIterator()
for item in collection:
  print(item)  # 1 2 3 4 5 

有了这些基础知识后,我们开始继续讲解,遍历list 的时候删除元素发生了什么?

arr = [1, 2, 3, 3, 4, 5, 3, 6] 以这个为例子吧

def delete_arr(arr: List[int]) -> List[int]:
    for num in arr:
        # print(f"num:{num}")
        if num == 3:
            arr.remove(num)
    return arr

当我们在列表中删除元素的时候 发生了什么? 第一 list 长度要减一,第二删除的元素 如果处于中间的元素, 那么之后的元素,就会依次前移 占中这个位置。 然后 for 循环 此时并不知道 list 的长度发生了改变。

下面 我画了一个图,解释一下。

1.当遍历到3 的时候,此时 有一个remove操作

![请添加图片描述](https://img-blog.csdnimg.cn/d30311102dd64ecc9467234cc5be2623.png)

2.当 list.remove(3)后, 后面的元素 整体前移。

在这里插入图片描述

3.此时下一个元素 为4, 注意 此时 第二个3 这个元素 ,就跳过去了.

在这里插入图片描述

4.随着迭代的不断进行,终于来到了, 最后一个3 的为位置

![在这里插入图片描述](https://img-blog.csdnimg.cn/6d3353f352f1415e8ae546abb5d7f06d.png#pic_center)

5.最后迭代到了最后一个元素

在这里插入图片描述

来分析一下:

当删除元素的时候,删除元素之后 位置 会前移, 此时迭代器并不能知道 当前的 list 会发生了变化,它只是调用next 方法 进行 迭代元素, 这样就有可能漏掉元素,比如3 会被漏掉, 第二点 remove(e) 这个方法,只会移除,第一个等于e 的元素,并且是从头开始 看,发现移除就返回了。

s.remove(element) 

 remove the **first item** from *s* where `s[i]` is equal to *x*

问题解决方案

复制一份数组

进行迭代 的时候 使用 副本 进行迭代,然后 删除元素还是在原来的基础上进行。

arr[:] 这个 就是 切片用法,默认就是 arr[0:lastIndex] , 这里相当于复制一份数据。

def delete_arr(arr: List[int]) -> List[int]:
    for num in arr[:]:
        if num == 3:
            arr.remove(num)
    return arr

总结

本文对for 循环了做了比较多的介绍,解释了for 循环如何运行的,知道如何实现一个迭代器,顺便解决了一个小的bug. 希望可以帮助到你,加油,同学。

之前写的一些比较旧的文章 仅供参考

python修改列表问题总结

python 列表总结二

分享快乐,留住感动. '2022-07-17 13:56:13' --frank
<think>嗯,用户现在问的是如何在Python遍历列表的所有元素。之前他们问过如何提取字符串中的括号内容,用的是正则表达式。现在这个问题看起来基础一些,但可能用户刚开始学Python,或者需要巩固基础。 首先,我需要确定用户的需求是什么。遍历列表是Python中最基本的操作之一,但用户可能不知道不同的遍历方法,或者想了解更高效的方式。可能的场景是用户有一个列表,想对每个元素进行操作,比如处理仪器地址,像之前的问题中的GPIB地址。 用户可能的真实需求不仅仅是知道如何遍历,而是想了解不同的遍历方法,比如按值、按索引,或者同时获取索引和值。此外,用户可能需要知道何时使用哪种方法,以及每种方法的优缺点。 接下来,我需要组织回答的结构。按照之前的风格,分步骤解释,使用示例代码,并给出结果验证。可能还需要补充注意事项,比如在遍历时修改列表的风险,或者使用列表推导式的高效方法。 另外,用户之前的问题涉及到了正则表达式,可能对字符串处理比较关注,所以在示例中可以结合字符串的例子,让用户更容易联系之前的知识点。 要确保回答清晰,覆盖基本方法,如for循环、while循环,以及高级方法如enumerate、列表推导式、map函数。同时提醒用户注意常见错误,比如在遍历时修改列表长度导致的错误。 最后,检查是否需要补充性能方面的建议,或者适用场景,帮助用户根据具体情况选择合适的方法。</think>--- 在 Python遍历列表的所有元素是最基础的操作之一。以下是 **6 种常用方法**,涵盖基本遍历、索引控制和高级用法: --- ### **方法 1:直接遍历元素(按值遍历)** 通过 `for item in list` 直接获取元素值: ```python devices = ["GPIB0::30::INSTR", "GPIB0::8::INSTR", "USB0::0x1234::INSTR"] for device in devices: print(device) ``` **输出**: ``` GPIB0::30::INSTR GPIB0::8::INSTR USB0::0x1234::INSTR ``` **适用场景**:只需元素值,无需索引。 --- ### **方法 2:通过索引遍历(按位置遍历)** 用 `range(len(list))` 获取索引,再通过索引访问元素: ```python for i in range(len(devices)): print(f"索引 {i}: {devices[i]}") ``` **输出**: ``` 索引 0: GPIB0::30::INSTR 索引 1: GPIB0::8::INSTR 索引 2: USB0::0x1234::INSTR ``` **适用场景**:需要同时知道元素值和索引。 --- ### **方法 3:使用 `enumerate()` 同时获取索引和值** 更 Pythonic 的写法,直接获得索引和元素: ```python for index, device in enumerate(devices): print(f"第 {index+1} 个设备地址: {device}") ``` **输出**: ``` 第 1 个设备地址: GPIB0::30::INSTR 第 2 个设备地址: GPIB0::8::INSTR 第 3 个设备地址: USB0::0x1234::INSTR ``` **优势**:代码简洁,无需手动处理索引。 --- ### **方法 4:`while` 循环遍历** 通过索引递增实现遍历: ```python i = 0 while i < len(devices): print(devices[i]) i += 1 ``` **适用场景**:需要更灵活控制遍历过程(如条件跳出)。 --- ### **方法 5:列表推导式(快速处理元素)** 对每个元素执行操作并生成新列表: ```python # 示例:提取所有设备的接口类型(如 GPIB、USB) interface_types = [device.split("::")[0] for device in devices] print(interface_types) # 输出: ['GPIB0', 'GPIB0', 'USB0'] ``` **优势**:单行代码完成遍历+转换。 --- ### **方法 6:`map()` 函数遍历** 配合 lambda 函数处理元素: ```python # 示例:将地址中的 "INSTR" 替换为 "INSTRUMENT" modified_devices = list(map(lambda x: x.replace("INSTR", "INSTRUMENT"), devices)) print(modified_devices) # 输出替换后的地址列表 ``` --- ### **注意事项** 1. **避免遍历时修改列表**: ```python # 错误示例:遍历删除元素会导致跳过或越界 for device in devices: if "GPIB" in device: devices.remove(device) # 危险操作! # 正确方法:遍历副本或新建列表 for device in devices.copy(): if "GPIB" in device: devices.remove(device) ``` 2. **性能对比**: - 直接遍历 (`for item in list`) 最快 - 列表推导式比 `map()` 更易读 - `while` 循环效率最低 --- ### **总结选择建议** | 需求 | 推荐方法 | |------|----------| | 只需元素值 | 直接遍历 (`for item in list`) | | 需要索引和值 | `enumerate(list)` | | 快速转换元素 | 列表推导式 | | 灵活控制流程 | `while` 循环 | 可以根据具体场景选择最适合的方法!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值