只需要索引:枚举还是(x)范围?

Posted

技术标签:

【中文标题】只需要索引:枚举还是(x)范围?【英文标题】:Only index needed: enumerate or (x)range? 【发布时间】: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()。) 【参考方案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】:

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

【讨论】:

以上是关于只需要索引:枚举还是(x)范围?的主要内容,如果未能解决你的问题,请参考以下文章

PySpark DataFrames - 在不转换为 Pandas 的情况下进行枚举的方法?

为啥在只需要一个字节时,Rust 使用两个字节来表示这个枚举?

The Super Powers UVA - 11752

用于解密的 Vigenere 密码“字符串索引超出范围”

高性能的索引策略2

CF851 D 枚举 思维