V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
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 · 113 天前 · 2295 次点击
    这是一个创建于 113 天前的主题,其中的信息可能已经有所发展或是发生改变。

    为什么会产生这个问题?

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

    # 如下是一个简单的循环
    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 条回复    2024-09-30 10:46:33 +08:00
    codehz
        1
    codehz  
       113 天前
    这就是为啥要删除的话推荐用列表推导式(
    实际上没几个语言能在迭代的时候修改原数组的吧(
    qianzanqi
        3
    qianzanqi  
       113 天前
    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  
       113 天前
    @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
       113 天前
    @lisongeee 确实很清楚的 index+1 qwq
    感谢大佬
    sgld
        6
    sgld  
    OP
       113 天前
    @qianzanqi 感谢大佬,源码大概意思能理解,明确指出了 index + 1 ,FT_ATOMIC_STORE_SSIZE_RELAXED 这些就去问大模型理解了,不太熟悉。
    index < 0 就会返回 NUll ,item == NULL 就把 index 设置为-1 。从而退出循环
    cybort
        8
    cybort  
       113 天前 via Android
    就算不用迭代器也不能一边循环一边 remove 啊
    shinession
        9
    shinession  
       112 天前   ❤️ 1
    按索引删除的话,要从最大到 0 删除,
    for i in range(len(l)-1,-1,-1):
    print(l[i])
    l.pop(i)
    print(l)
    EndlessMemory
        10
    EndlessMemory  
       112 天前   ❤️ 1
    再遍历某个可迭代对象的时候,不要做删除操作
    Lhcfl
        11
    Lhcfl  
       112 天前
    不要写这样的代码,会跑出 O(n^2 )的时间复杂度,而且如果是 C++的话这是典型的 undefined behaviour ,以及都用 python 了研究迭代器怎么迭代的干什么,把它当成黑盒模型用就好了
    julyclyde
        12
    julyclyde  
       112 天前
    所谓指针这个明显是臆测啊
    那只是其中一种实现方法,但并不是必须这么实现
    deplives
        13
    deplives  
       112 天前
    众所周知,遍历对象的时候不要做删除操作
    delphisharp
        14
    delphisharp  
       79 天前
    不要在遍历时做改变遍历对象长度的操作。会给自己挖坑的。
    就仅仅做打印删除的场景,我用 while 来实现。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1022 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 20:35 · PVG 04:35 · LAX 12:35 · JFK 15:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.