【问题标题】:For Loop Enumerate vs dict on enumerateFor Loop Enumerate vs dict on enumerate
【发布时间】:2020-11-12 14:05:09
【问题描述】:

让我先声明一下我在 Python 和一般编程方面都是新手。也就是说,我想知道以下两种基于现有列表创建字典的方法之间是否有任何区别:

index = {}
seq = ['a', 'b', 'c']
for a,b in enumerate(seq): 
    index[a]= b
print(index)
print(dict(enumerate(seq)))

输出:

>>> print(index)
{0: 'a', 1: 'b', 2: 'c'}
>>> print(dict(enumerate(seq)))
{0: 'a', 1: 'b', 2: 'c'}

“print (index)”和“print (dict (enumerate (seq)”) 都产生了等效的结果,我想知道在这种情况下哪个更好。我通过练习一些编码偶然发现了后者这让我产生疑问,如果像 dict() 这样的单行代码给出相同的结果,为什么需要执行 for 循环。

【问题讨论】:

  • 请将问题中的代码缩进与您正在运行的代码相同。
  • 你自己写的,没理由写循环。
  • print 并不真正相关。您可以编写 index = dict(enumerate(seq)) 作为完整 for 循环的更简洁替代方案。
  • 这是一个有趣的问题。我的观点:-)

标签: python dictionary for-loop enumerate


【解决方案1】:

我见过很多单行循环。它只是用作定义某些东西的更编译的方式,但在大多数情况下,for 循环同样好。

【讨论】:

    【解决方案2】:

    dict(enumerate(seq)) 在性能和简单性方面更胜一筹,只是不够灵活。为了获得更大的灵活性,您通常会使用 dict 理解,例如:

    index = {i: x * 2 for i, x in enumerate(seq)}  # Repeats each value
    

    只有当转换代码太复杂而无法理解时,才会转到原来的 for 循环。

    【讨论】:

    • 好吧..令人惊讶的是,它似乎并没有更快...
    • @thebjorn:较大的输入会稍微快一些。对于小的输入,通过泛型函数分派调用dict 构造函数的微小固定开销可以使它变得非常慢。
    【解决方案3】:

    输出完全没有区别。

    关于计时,您可以使用timeit模块来测试速度。

    第二个选项,一个衬里一定会更快,并且在我看来也更具可读性。

    计时结果,Python 3.9:

    python -m timeit -s "seq = ['a', 'b', 'c']" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    500000 loops, best of 5: 803 nsec per loop
    
    python -m timeit -s "seq = ['a', 'b', 'c']" "dict(enumerate(seq))"
    500000 loops, best of 5: 786 nsec per loop
    

    您可能看不到这里的区别,因为用于创建 dict 的 dict() 操作比 {} 长,而且它是固定的“设置成本”。

    如果您增加数量来抵消这种“设置成本”,您会看到巨大的差异:

    python -m timeit -s "seq = ['a', 'b', 'c']*1000" "dict(enumerate(seq))"
    500 loops, best of 5: 395 usec per loop
    
    python -m timeit -s "seq = ['a', 'b', 'c']*1000" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    500 loops, best of 5: 509 usec per loop
    

    【讨论】:

    • 哪个python版本?
    • @thebjorn 3.9b1。由于某种原因,感觉差距比我预期的要小。不是吗?这个我还没编。这是 pyorg 的纯构建
    • 在 3.5 上我发现 for 循环更快...我现在正在仔细检查我的结果...
    • @thebjorn 请记住,dict 不是设置的一部分。哦,dict() 确实需要 LOAD_GLOBAL & func 调用而不是纯操作码。也许这就是差异较小的原因?我敢打赌,数字越大,它会越大。
    • 差距很小,因为数据很小。这并不是一个真正相关的配置文件,诸如函数调用开销之类的东西开始占主导地位。
    【解决方案4】:

    没有语义差异。这个

    dict(enumerate(seq))
    

    会执行得更快。

    更新:好吧,似乎更快可能不像我预期的那样明确..(或正确,除了可能是 3.9b1 - 请参阅@Bharel 的回答)。

    Update2:为了完整性,我已经为来自 @ShadowRanger 的 dict 理解解决方案添加了测量值。

    Py2.7:

    (dev) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "dict(enumerate(seq))"
    1000000 loops, best of 3: 1.06 usec per loop
    
    (dev) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    1000000 loops, best of 3: 0.686 usec per loop
    
    (dev) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "{a: b for a,b in enumerate(seq)}"
    1000000 loops, best of 3: 0.807 usec per loop
    

    Py3.5:

    (dev35) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "dict(enumerate(seq))"
    1000000 loops, best of 3: 1.19 usec per loop
    
    (dev35) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    1000000 loops, best of 3: 0.813 usec per loop
    
    (dev35) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "{a: b for a,b in enumerate(seq)}"
    1000000 loops, best of 3: 0.917 usec per loop
    

    Py3.8:

    (dev38) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "dict(enumerate(seq))"
    500000 loops, best of 5: 755 nsec per loop
    
    (dev38) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    500000 loops, best of 5: 703 nsec per loop
    
    (dev38) c:\srv> python -m timeit -s "seq = ['a', 'b', 'c']" "{a: b for a,b in enumerate(seq)}"
    500000 loops, best of 5: 879 nsec per loop
    

    更新 3: @Bharel 和 @ShadowRanger 指出,列表的长度很重要。 dict(enumerate(..)) 版本的列表大约有 10 个项目,性能更高。

    代码:

    call workon dev
    python -m timeit -s "seq = 'abcdefghij'" "dict(enumerate(seq))"
    python -m timeit -s "seq = 'abcdefghij'" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    
    call workon dev35
    python -m timeit -s "seq = 'abcdefghij'" "dict(enumerate(seq))"
    python -m timeit -s "seq = 'abcdefghij'" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    
    call workon dev38
    python -m timeit -s "seq = 'abcdefghij'" "dict(enumerate(seq))"
    python -m timeit -s "seq = 'abcdefghij'" "index = {}" "for a,b in enumerate(seq):" "  index[a]= b"
    

    输出:

    100000 loops, best of 3: 1.94 usec per loop
    1000000 loops, best of 3: 1.9 usec per loop
    
    1000000 loops, best of 3: 1.7 usec per loop
    1000000 loops, best of 3: 1.6 usec per loop
    
    200000 loops, best of 5: 1.29 usec per loop
    200000 loops, best of 5: 1.66 usec per loop
    

    【讨论】:

    • 伙计,您正在设置时创建字典。您在第一次测试中一次又一次地使用相同的字典,而在第二次每次都创建一个字典。这些测试不相等。
    • @Bharel 啊,谢谢。运行您的代码,我仍然认为 for 循环对于 2.7、3.5、3.8 来说更快。
    • @thebjorn:为中等数量的项目更新测试;对于 50 件左右的物品,dict(enumerate(seq)) 应该会赢(这还不够重要,但仍然有很大的优势)。只有三个,固定开销淹没了实际工作。
    • @ShadowRanger 我确定它依赖于机器,但在我的机器上它只需要大约 10'ish 元素......
    • @thebjorn:这一次我实际上并没有计时,我之前只是这样做过,并且给出了一个足够大的数字来明确显示效果。 10 并不让我感到惊讶。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    相关资源
    最近更新 更多