我们中的许多人都知道,enumerate
正在使用for
循环并且需要知道索引的情况。但是,它有缺点。根据我对timeit
模块的测试,仅使用enumerate
会使代码速度变慢2倍。添加这个元组赋值使其速度降低3倍。对于任何程序员来说,这些数字可能足够快,但是处理算法的人知道你可以优化的每一段代码都是巨大的优势。现在问我的问题,
这种用法的一个例子是,需要在list
中找到多个元素的索引。假设我们需要找到两个元素。我遇到的前两个解决方案是这样的:
x, y = 0, 0
for ind, val in enumerate(lst):
if x and y:
break
if val == "a":
x = ind
elif val == "b":
y = ind
上面的解决方案迭代列表,分配值,而不是在找到两个值时中断。
x = lst.index("a")
y = lst.index("b")
这是另一种解决方案,我不想使用,因为它看起来很天真。它遍历同一个列表两次,找到两个元素。第一个解决方案,在一次迭代中完成。因此,就复杂性而言,即使我们在第一个解决方案中进行额外的分配,它也应该比较大的列表中的第二个更快。但我的假设失败。
以下是我测试效果的代码: https://codeshare.io/XfvGA
第二种解决方案比第一种解决方案快2到10倍,随着这两种元素的位置而变化。有几种可能性会发生这种情况。
index()
方法的优化。index()
方法中进行较低级别的分配。可能使用C ++代码。即使这些原因也无法解释迭代列表两次的速度,而不是迭代一次。虽然语言在运行代码时在时间上有很大差异,但迭代过程本身与编程语言无关,如果你需要检查一百万个元素,你还需要检查一百万个元素(可以通过map()
来代替比使用循环更改值快得多。)
因此,虽然我需要你检查我提出的案例,但为了澄清这里提出的问题,可以像这样把问题放在一起。我们知道Python的for
循环实际上是在后台运行的while
(可能在C?中)。所以这意味着,索引正在存储,因为它在内存中的某处递增。如果有办法访问它,这将消除调用和解包enumerate
的成本。我的问题是:
是否存在这样的方式?如果没有,可以(为什么,或为什么不)?
我使用的来源有关该主题的更多信息:
答案 0 :(得分:1)
我不认为enumerate
是问题,要证明你可以这样做:
x, y = 0, 0
for val in a:
if x and y:
break
if val == "a":
x = val
elif val == "b":
y = val
这并不是你想要的第一个东西(你没有得到索引)但是如果你把它与timeit混淆,你会发现差异不那么重要,这意味着enumerate
是不是问题的根源(在我的情况下,运行你的例子时它是0.185到0.155,所以它更快但第二个解决方案在我的计算机上得到0.055)
lst.index
更快的原因是它是用C实现的。
你可以在这里看到它的源代码:
https://svn.python.org/projects/python/trunk/Objects/listobject.c
index函数在此文件中称为listindex
,定义类似于
static PyObject *
listindex(PyListObject *self, PyObject *args)
(我找不到直接添加链接到函数的方法)
答案 1 :(得分:0)
你正试图成为非Pythonic,这对你来说并不会非常好。如果您确实需要提供迭代器计数信息,那么有一种众所周知且经过优化的方法:enumerate()
。如果您需要在列表中查找项目,则有一种众所周知且经过优化的方法:lst.index()
。正如DorElias在上方/下方显示的那样,enumerate
不是问题,而是您尝试使用for
循环的其余部分重新发明轮子。 enumerate
将成为最佳支持(最清晰,最快速等)的方式,以便在每种情况下维持迭代计数,其中迭代计数实际上是您需要的。