V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
sgld
V2EX  ›  Python

for 循环里面的迭代器到底怎么迭代的

  •  
  •   sgld · Aug 27, 2024 · 3987 views
    This topic created in 611 days ago, the information mentioned may be changed or developed.

    为什么会产生这个问题?

    因为我写了一个有问题的代码,大概长下面这样

    # 如下是一个简单的循环
    l = ['abc' ,2 ,[1, 2, 3]  ,4]
    for i in l:
        print(i)
        l.remove(i)
    print(l)
    

    最后的输出是

    abc
    [1, 2, 3]
    [2, 4]
    

    踩坑的原因是,在遍历的时候,我根据一些条件去删除列表中的元素,然后偶然发现输出与我的预期并不符合。

    原本的理解是next会依次返回后面一个元素,因此现在这个元素我删了也没事。

    但是事实显然不是这样,列表产生的迭代器返回值好像是根据索引返回的

    大模型的回答不同模型直接存在差异,因此在翻了一些 blog ,有一篇里面有句话如下所示

    ==迭代器本身并不存储任何数据项,存储的只是一个指针,该指针指向可迭代对象中真正存储的数据项,它指向当前被遍历到的数据项索引位置,下一次遍历则向后推进这个位置==

    所以再次来求助一下,迭代器的 next 是按照索引来获取数据的吗

    14 replies    2024-09-30 10:46:33 +08:00
    codehz
        1
    codehz  
       Aug 27, 2024
    这就是为啥要删除的话推荐用列表推导式(
    实际上没几个语言能在迭代的时候修改原数组的吧(
    qianzanqi
        3
    qianzanqi  
       Aug 27, 2024
    list_iterator 也就是 type(iter([]))定义在
    https://github.com/python/cpython/blob/main/Include/internal/pycore_list.h#L57
    成员变量是索引和原本 list

    __next__逻辑在
    https://github.com/python/cpython/blob/main/Objects/listobject.c#L3872
    用 PyObject *item = list_get_item_ref(seq, index);获取元素,list_get_item_ref 在读取非法 index 时返回 NULL 从而结束 next 调用。执行完一次 next 后,list_iterator 储存的 index+1

    你的例子中,一共调用了 2 次 next
    第一次结束 index=0 list=[2, [1, 2, 3], 4]
    第二次结束 index=1 list=[2, 4]
    第三次调用 next 时,index=2 非法,结束迭代
    qianzanqi
        4
    qianzanqi  
       Aug 27, 2024
    @qianzanqi 说例子时的 index 错了
    第一次开始 index=0 ,结束时 index=1 list=[2, [1, 2, 3], 4]
    第二次开始 index=1 ,结束时 index=2 list=[2, 4]
    第三次 index=2 非法
    sgld
        5
    sgld  
    OP
       Aug 27, 2024
    @lisongeee 确实很清楚的 index+1 qwq
    感谢大佬
    sgld
        6
    sgld  
    OP
       Aug 27, 2024
    @qianzanqi 感谢大佬,源码大概意思能理解,明确指出了 index + 1 ,FT_ATOMIC_STORE_SSIZE_RELAXED 这些就去问大模型理解了,不太熟悉。
    index < 0 就会返回 NUll ,item == NULL 就把 index 设置为-1 。从而退出循环
    cybort
        8
    cybort  
       Aug 28, 2024 via Android
    就算不用迭代器也不能一边循环一边 remove 啊
    shinession
        9
    shinession  
       Aug 28, 2024   ❤️ 1
    按索引删除的话,要从最大到 0 删除,
    for i in range(len(l)-1,-1,-1):
    print(l[i])
    l.pop(i)
    print(l)
    EndlessMemory
        10
    EndlessMemory  
       Aug 28, 2024   ❤️ 1
    再遍历某个可迭代对象的时候,不要做删除操作
    Lhcfl
        11
    Lhcfl  
       Aug 28, 2024
    不要写这样的代码,会跑出 O(n^2 )的时间复杂度,而且如果是 C++的话这是典型的 undefined behaviour ,以及都用 python 了研究迭代器怎么迭代的干什么,把它当成黑盒模型用就好了
    julyclyde
        12
    julyclyde  
       Aug 28, 2024
    所谓指针这个明显是臆测啊
    那只是其中一种实现方法,但并不是必须这么实现
    deplives
        13
    deplives  
       Aug 28, 2024
    众所周知,遍历对象的时候不要做删除操作
    delphisharp
        14
    delphisharp  
       Sep 30, 2024
    不要在遍历时做改变遍历对象长度的操作。会给自己挖坑的。
    就仅仅做打印删除的场景,我用 while 来实现。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1003 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 67ms · UTC 18:25 · PVG 02:25 · LAX 11:25 · JFK 14:25
    ♥ Do have faith in what you're doing.