【问题标题】:Only index needed: enumerate or (x)range?只需要索引:枚举还是(x)范围?
【发布时间】:2012-08-07 17:18:41
【问题描述】:

如果我只想在循环中使用索引,我应该更好地将range/xrange 函数与len() 结合使用

a = [1,2,3]
for i in xrange(len(a)):
    print i 

还是enumerate?即使我根本不会使用p

for i,p in enumerate(a):
    print i    

【问题讨论】:

  • 我真的很好奇你的用例是什么。
  • 我遇到了一些代码,实际上 enumerate 不应该在第一名 [[profiel.attr[i].x for i,p in enumerate(profiel.attr)] for profiel in prof_obj] 中使用。不需要p,或者应该是[[p.attr.x for p in profiel.attr] for profiel in prof_obj]。所以我问自己应该以一种或另一种方式重写代码......
  • 这段代码实际上应该是[[p.x for p in profiel.attr] for profiel in prof_obj]
  • 没错,我的错。不能再编辑了,谢谢你纠正这个问题。
  • @Sven Marnach,最近我做了一些编码,实际上我只需要索引来访问数组切片,如下所示:sum_dist = [[sum(afst[:i]) for i,_ in enumerate(afst,start=1)] for afst in dist_betw]。 (尽管我知道这个构造并不是真正需要的,因为我也可以使用 itertools.accumlate()。)

标签: python range enumerate


【解决方案1】:

我进行了时间测试,发现范围比枚举快了大约 2 倍。 (在 Python 3.6 for Win32 上)

最好的 3,对于 len(a) = 1M

  • 枚举(a):0.125s
  • 范围(len(a)): 0.058s

希望对你有帮助。

仅供参考:我最初开始这个测试是为了比较 python 和 vba 的速度...发现 vba 实际上比 range 方法快 7 倍...是因为我的 Python 技能差吗?

在某种程度上,python 肯定比 vba 做得更好

枚举脚本

import time
a = [0]
a = a * 1000000
time.perf_counter()

for i,j in enumerate(a):
    pass

print(time.perf_counter())

范围脚本

import time
a = [0]
a = a * 1000000
time.perf_counter()

for i in range(len(a)):
    pass

print(time.perf_counter())

vba 脚本 (0.008s)

Sub timetest_for()
Dim a(1000000) As Byte
Dim i As Long
tproc = Timer
For i = 1 To UBound(a)
Next i
Debug.Print Timer - tproc
End Sub

【讨论】:

    【解决方案2】:

    我写这个是因为我想测试它。 因此,这取决于您是否需要使用这些值。

    代码:

    testlist = []
    for i in range(10000):
        testlist.append(i)
    
    def rangelist():
        a = 0
        for i in range(len(testlist)):
            a += i
            a = testlist[i] + 1   # Comment this line for example for testing
    
    def enumlist():
        b = 0
        for i, x in enumerate(testlist):
            b += i
            b = x + 1   # Comment this line for example for testing
    
    import timeit
    t = timeit.Timer(lambda: rangelist())
    print("range(len()):")
    print(t.timeit(number=10000))
    t = timeit.Timer(lambda: enumlist())
    print("enum():")
    print(t.timeit(number=10000))
    

    现在您可以运行它,并且很可能会得到结果,即 enum() 更快。 当您在 a = testlist[i] + 1b = x + 1 评论源代码时,您会看到 range(len()) 更快。

    对于上面的代码,我得到:

    range(len()):
    18.766527627612255
    enum():
    15.353173553868345
    

    现在当如上所述发表评论时,我得到:

    range(len()):
    8.231641875551514
    enum():
    9.974262515773656
    

    【讨论】:

    • 我认为您应该添加一个帮助说明,该测试显示当您访问列表元素时枚举速度更快,而当您不访问时 range(len) 速度更快。
    【解决方案3】:

    根据您的示例代码,

    res = [[profiel.attr[i].x for i,p in enumerate(profiel.attr)] for profiel in prof_obj]
    

    我会替换成

    res = [[p.x for p in profiel.attr] for profiel in prof_obj]
    

    【讨论】:

      【解决方案4】:

      xrange 应该快一点,但是 enumerate 意味着当你意识到你需要p 毕竟不需要改变它

      【讨论】:

      • 但如果/当您决定这样做时,这是一个微不足道的改变,所以我不会单独在此基础上进行。
      【解决方案5】:

      我会使用enumerate,因为它更通用——例如,它适用于可迭代对象和序列,并且仅返回对对象的引用的开销并不是什么大问题——尽管xrange(len(something))(对我来说) 更易于阅读,因为您的意图 - 将在不支持 len 的对象上中断...

      【讨论】:

      • 非常有趣的一点。哪个是不支持len() 的对象示例?一个函数?
      • @larsvegas itertools.count(10) 这是一个发电机
      • @jamylak:注意itertools.count(10) 是一个无限生成器,所以你也不想枚举它。
      • @jamylak:是的,或者iter([]) 更简洁。 :)
      【解决方案6】:

      将 xrange 与 len 一起使用是很常见的用例,所以是的,如果您只需要按索引访问值,则可以使用它。

      但如果您出于某种原因更喜欢使用枚举,则可以使用下划线 (_),它只是一种常见的符号,表明您不会以某种有意义的方式使用该变量:

      for i, _ in enumerate(a):
          print i
      

      还有一个使用下划线 (_) 可能会发生的陷阱。在 i18n 库和系统中,将“翻译”函数命名为 _ 也很常见,因此请注意将其与 gettext 或其他此类库一起使用(感谢 @lazyr)。

      【讨论】:

      • 请注意不要将此成语与gettext 结合使用,因为它会将_ 变量用于其他用途,并且此使用会影响当前命名空间中的gettext _。这可能会导致奇怪的错误。
      • @jamylak Nope
      • 不使用_ 作为变量名的最重要原因是人们对它有各种奇怪的误解,并倾向于将其误认为是某种特殊语法。我已经看到 很多 人对此感到困惑,所以我只需将其命名为 dummy 即可避免这种困惑。显式优于隐式。
      • @jamylak:后者也是我对未使用名称所做的事情。将其命名为dummy 的建议只是因为人们倾向于认为_ 清楚地表明该变量是一个虚拟变量。 (为什么这会“清楚”是他们的秘密。)此外,一些 IDE 会警告未使用的变量,并且通常会忽略一些名称模式,例如 unused_xxx 或类似的。
      • @jamylak。我关于 IDE 的 5 美分。默认情况下,Eclipse-PyDEV/Aptana 在查找未使用的名称时会出现下划线异常(不计入)。
      【解决方案7】:

      只需使用range()。如果您无论如何都要使用所有索引,xrange() 并没有提供真正的好处(除非len(a) 真的很大)。而enumerate() 创建了一个更丰富的数据结构,您将立即丢弃。

      【讨论】:

      • xrange() 提供了非常大的好处!它不会在内存中创建临时列表,它是一个生成器
      • 不适用于此要求。 OP 只是创建了一个索引范围。
      • @RajeshJAdvani 不,他正在逐一遍历并打印它们。
      • 尽管如此,它只是一个数字列表。但是,是的,如果它是一个非常大的数组,那么 xrange 会很有用。更新了我的答案以反映这一点。
      • @RostyslavDzinko xrange 不是生成器。它是一个惰性求值的序列对象。
      【解决方案8】:

      这是一个罕见的要求——容器中唯一使用的信息就是它的长度!在这种情况下,我确实会明确说明这一事实并使用第一个版本。

      【讨论】:

        猜你喜欢
        • 2012-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-30
        • 1970-01-01
        • 1970-01-01
        • 2014-04-20
        • 1970-01-01
        相关资源
        最近更新 更多