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
smallgoogle
V2EX  ›  Python

我怀疑这是 Python for 的一个 bug

  •  
  •   smallgoogle · 2020-04-18 16:04:36 +08:00 · 2500 次点击
    这是一个创建于 1712 天前的主题,其中的信息可能已经有所发展或是发生改变。
    # -*- coding: utf-8 -*-
    
    from multiprocessing import Pool
    import os, time, random
    
    
    def worker(msg, ip):
        t_start = time.time()
        print("%s 开始执行, ip:%s, 进程号为%d" % (msg['id'], ip['ip'], os.getpid()))
        time.sleep(random.random() * 2)
        # time.sleep(2)
        t_stop = time.time()
        print(msg['id'], "执行完毕, ip:%s, 耗时%0.2f" % (ip['ip'], t_stop - t_start))
    
    
    def start(d):
        po = Pool(10)
        a_list = [
            {"ip": "ip - 1"},
            {"ip": "ip - 2"}
        ]
        d_list = d
        for_times = 0
        while len(d_list) > 0:
            print("第 %s 次" % (for_times))
            x = 0
            for i in d_list:
                if x < 2:
                    po.apply_async(worker, (i, a_list[for_times]))
                    d_list.pop(x)
                    x+=1
                else:
                    print("==============")
                    break
            for_times += 1
        print("----start----")
        po.close()
        po.join()
        print("----end----")
    
    
    
    if __name__ == '__main__':
        d = [{
            "id": 1,
            "keyword": "site:www.aaa.com 灯",
            "site": "www.aaa.com"
        }, {
            "id": 2,
            "keyword": "site:www.bbb.com 灯",
            "site": "www.bbb.com"
        }, {
            "id": 3,
            "keyword": "site:www.ccc.com 灯",
            "site": "www.ccc.com"
        }, {
            "id": 4,
            "keyword": "site:www.ddd.com 灯",
            "site": "www.ddd.com"
        }]
        start(d)
    

    这段代码没问题。可是会报错;我 debug 了。就是 for 里面的 if 的问题。
    理论上 whlie 两次就结束了。可是为啥这里 whlie 走了三次呢?
    while 第二次的时候,会看到 for 里面的 if 没生效了。
    这是不是 python 的 bug ?

    我已经排两天。无结果。

    第 1 条附言  ·  2020-04-18 16:46:29 +08:00
    采纳七楼的方法,果然是酱紫的。

    只是我还是不能理解为什么不能按照原来的办法做,我认为逻辑上是没有问题的;

    ===========

    @Jirajine
    你在迭代时修改了迭代对象本身,当然会产生难以预料的行为。
    一个简单的做法是在目标对象的复制上迭代,
    将 for i in d_list: 修改为 for i in list(d_list):
    第 2 条附言  ·  2020-04-18 16:51:06 +08:00
    大佬们。我知道问题在哪了。
    还是评论大神多。果然是 pop 的下标问题。其他的都不是问题;

    最终问题是 pop 的下标。。我蠢了。对不起大家。耽误了大家时间。。标题过分了。
    GrayXu
        1
    GrayXu  
       2020-04-18 16:21:28 +08:00
    没看代码…但觉得是 python 的 bug ?
    yingo
        2
    yingo  
       2020-04-18 16:24:38 +08:00
    第 0 次
    第 1 次
    第 2 次
    Traceback (most recent call last):
    File "D:\python\dict.py", line 61, in <module>
    start(d)
    File "D:\python\dict.py", line 29, in start
    po.apply_async(worker, (i, a_list[for_times]))
    IndexError: list index out of range

    for i in d_list:
    ...
    d_list.pop()
    ...

    别写这种代码.
    renmu
        3
    renmu  
       2020-04-18 16:28:45 +08:00 via Android
    不要一边对列表修改一边进行判断,会引起奇怪的问题。想复制的话可以采用 list.copy(),而不是直接等于
    NeinChn
        4
    NeinChn  
       2020-04-18 16:29:37 +08:00
    d_list.pop(x)是咋想的。。。
    xiri
        5
    xiri  
       2020-04-18 16:30:00 +08:00
    调试看了一下,没问题啊,最后是数组越界了,主要就是你在 for 循环里面弹出 d_list 中的元素导致的,你自己去看看每次循环的时候 d_list 变成啥样了,i 是啥就知道了,还有,最好不要写下面这样的代码,出问题能让你头痛死
    for i in d_list:
    ......
    d_list.pop(x)
    ......
    DGideas
        6
    DGideas  
       2020-04-18 16:32:02 +08:00
    你的程序的输出结果是什么?

    贴代码到这里来问问题,没有描述解释器和操作系统环境,别人复现你的问题也不一定准啊
    Jirajine
        7
    Jirajine  
       2020-04-18 16:32:19 +08:00 via Android
    你在迭代时修改了迭代对象本身,当然会产生难以预料的行为。
    一个简单的做法是在目标对象的复制上迭代,
    将 for i in d_list: 修改为 for i in list(d_list):
    wysnylc
        8
    wysnylc  
       2020-04-18 16:32:42 +08:00
    人类总是觉得机器有问题而不是自己有问题
    yingxiangyu
        9
    yingxiangyu  
       2020-04-18 16:36:48 +08:00
    循环过程中不要修改循环的列表,你 pop 了列表就变了,第二次循环开始是 2 元素,pop 一个变 1 元素,这个 for 还要不要接着执行
    imn1
        10
    imn1  
       2020-04-18 16:41:19 +08:00
    看标题还没看正文就想,是不是又是增减列表的问题
    进来一看果然……
    freakxx
        11
    freakxx  
       2020-04-18 16:43:40 +08:00
    首先这标题就够标题了

    import 的时候,这样写是好的
    import os
    import random
    import time

    然后
    都用到 py3 了,多用用 f,或者 format

    要随机 1-2s
    直接用 random.uniform(0, 2)


    while 嵌套 for 这种也挺不好看的

    不需要特殊判断的话 while d_list 就可以了

    需要判断第 N 次的话,直接用
    for index, value in enumerate(d_list)

    不要一边遍历 list 一般修改




    ==============

    所以不要搞个大新闻
    superrichman
        12
    superrichman  
       2020-04-18 16:46:51 +08:00
    d_list.pop(x) 问题出在这里,仔细琢磨一下你要 pop 出来的元素和下标为 x 的是不是同一个。
    smallgoogle
        13
    smallgoogle  
    OP
       2020-04-18 16:48:37 +08:00
    @freakxx 我的需求实际上用 enumerate 是无法解决的。 = = 还是需要用 list 。

    @imn1 所以你有啥解决方案?我比较想优雅的解决。
    cassidyhere
        14
    cassidyhere  
       2020-04-18 19:49:15 +08:00
    用队列。。。
    ifzzzh
        15
    ifzzzh  
       2020-04-18 22:13:51 +08:00
    @smallgoogle #13 你都用 x<2 做判断了为什么不直接拿 x 做循环变量呢
    ieric
        16
    ieric  
       2020-04-18 23:56:13 +08:00 via iPhone
    @freakxx 还有多余代码 d_list=d ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2505 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 15:55 · PVG 23:55 · LAX 07:55 · JFK 10:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.