【问题标题】:Python - shortening an if / for loopPython - 缩短 if / for 循环
【发布时间】:2011-12-23 21:15:15
【问题描述】:

我有几行代码用于迭代列表中的字典,我希望缩短它。它按原样完美运行,但似乎代码太多,我正在尝试了解如何在 Python 中保持代码高效(或实际上)。

for d in dev['devices']:
    if d['name'] == devName:
        devFound = True
        break

“dev”的结构有点混乱,但对于我关心的数据: dev (dict) > devices (list) > 0-n (dict)

值 'name' 是内部编号字典中的一个键(取决于其他地方的搜索值),需要根据用户输入 (devName) 进行检查

任何意见都非常感谢

【问题讨论】:

    标签: python coding-style if-statement for-loop iterator


    【解决方案1】:

    基本相同,只是重写了一些内置函数和生成器:

    devFound = any(d['name'] == devName for d in dev['devices'])
    

    【讨论】:

    • PEP 8 不喜欢any 之后的空格。想要修复它以获得支持?
    【解决方案2】:

    你可以试试any()函数:

    any(d for d in dev["devices"] if d['name'] == devName)
    

    【讨论】:

    • 感谢您的输入,看起来与@doublep 的想法相同,但措辞略有不同。谢谢!
    • 我更喜欢 doublep 的解决方案,因为它把真相测试放在了前面,这似乎对any 更有意义。我确实喜欢你链接到文档,所以无论如何都要 +1。
    • @SG84:这个可能效率更高一点,但不那么直接。
    • @Cédric Julien:我真的不明白编写返回匹配ds(dany 之后)的生成器表达式的意义,作为匹配@ 的内容987654328@ 没关系。因此,我发现这个解决方案令人困惑。最重要的是,如果 dev["devices"] 包含真值为 False 的映射对象(参见 docs.python.org/library/stdtypes.html),它会失败——虽然这很不寻常,但官方并未排除这种情况。
    • @EOL:Cédric 的解决方案迭代基本上停止在第一个找到的项目上。所有循环都在运行时内以高度优化的 C 代码完成。另一方面,在我的示例中,每次迭代都会评估一些 Python 代码,虽然很简单,但它仍然不如“内置”if 快。
    【解决方案3】:

    这是 Cédric Julien 答案的变体,因为它在某些(罕见)情况下可能会失败:

    any(True for d in dev["devices"] if d['name'] == devName)
    

    这是一个(虽然不寻常,但可能)案例,说明 any(True …) 给出正确结果,而 any(d …) 没有:

    >>> class special_dict(dict):
    ...     def __nonzero__(self):
    ...         return False  # All special_dict objects are False
    ...     
    >>> dev = {'devices': [special_dict(name="DEVNAME") for _ in xrange(10)]}
    >>> any(d for d in dev["devices"] if d['name'] == "DEVNAME")  # Incorrect
    False
    >>> any(True for d in dev["devices"] if d['name'] == "DEVNAME")  # Correct
    True
    

    事实上,special_dict 对象的计算结果为 False,因此在any() 中测试d 的真值是没有意义的。不过,使用 True 可以。

    PS:时序测试表明any(True … for … if … == …) 方法比doublep 的any(… == … for …) 解决方案更快:

    python -m timeit -s "dev = {'devices': [{'name': 'BADNAME'} for _ in xrange(100)]}" "any(d['name'] == 'DEVNAME' for d in dev['devices'])"
    100000 loops, best of 3: 16.3 usec per loop
    
    python -m timeit -s "dev = {'devices': [{'name': 'BADNAME'} for _ in xrange(100)]}" "any(True for d in dev['devices'] if d['name'] == 'DEVNAME' )"
    100000 loops, best of 3: 9.42 usec per loop
    

    原因是第二个生成器最多返回一个值(True)。这可以通过反汇编两个生成器的 Python 代码来看出:

    In [8]: def f(my_list):
       ...:     return any(x == 11 for x in my_list)
    In [12]: f.func_code.co_consts[1]
    Out[12]: <code object <genexpr> at 0x1041f98b0, file "<ipython-input-8-384ce7986872>", line 2>
    In [13]: dis.dis(_)
      2           0 LOAD_FAST                0 (.0)
            >>    3 FOR_ITER                17 (to 23)
                  6 STORE_FAST               1 (x)
                  9 LOAD_FAST                1 (x)
                 12 LOAD_CONST               0 (11)
                 15 COMPARE_OP               2 (==)
                 18 YIELD_VALUE         
                 19 POP_TOP             
                 20 JUMP_ABSOLUTE            3
            >>   23 LOAD_CONST               1 (None)
                 26 RETURN_VALUE        
    

    此代码包含YIELD_VALUEPOP_TOP,与此答案的版本相比,这需要更多时间:

    In [14]: def g(my_list):
       ....:     return any(True for x in my_list if x == 11)
    
    In [15]: g.func_code.co_consts[1]
    Out[15]: <code object <genexpr> at 0x1041f9630, file "<ipython-input-14-735c68947d80>", line 2>
    In [16]: dis.dis(g.func_code.co_consts[1])
      2           0 LOAD_FAST                0 (.0)
            >>    3 FOR_ITER                23 (to 29)
                  6 STORE_FAST               1 (x)
                  9 LOAD_FAST                1 (x)
                 12 LOAD_CONST               0 (11)
                 15 COMPARE_OP               2 (==)
                 18 POP_JUMP_IF_FALSE        3
                 21 LOAD_GLOBAL              0 (True)
                 24 YIELD_VALUE         
                 25 POP_TOP             
                 26 JUMP_ABSOLUTE            3
            >>   29 LOAD_CONST               1 (None)
                 32 RETURN_VALUE        
    

    【讨论】:

    • 你说得对 d 的情况是不可信的(我有一个 C 绑定没有正确处理 __nonzero__ 的情况)。除此之外,它更快的事实让我感到惊讶。
    猜你喜欢
    • 2022-01-10
    • 2019-02-27
    • 2016-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-12
    相关资源
    最近更新 更多