Python for循环索引,没有枚举

时间:2016-10-29 13:55:47

标签: python performance python-2.7 time-complexity

我们中的许多人都知道,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的成本。我的问题是:

是否存在这样的方式?如果没有,可以(为什么,或为什么不)?

我使用的来源有关该主题的更多信息:

Python speed

Python objects time complexity

Performance tips for Python

2 个答案:

答案 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将成为最佳支持(最清晰,最快速等)的方式,以便在每种情况下维持迭代计数,其中迭代计数实际上是您需要的