只需要索引:枚举还是(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] + 1
和 b = 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 的情况下进行枚举的方法?