for / in / if如果列表理解变得很慢,匹配次数很多

时间:2016-05-22 17:01:45

标签: python performance list list-comprehension

我的Python 2.7代码中有以下列表理解,它返回行号(索引)和一长串行中的行:

if (localStorage.getItem("name1") != null) {
  document.getElementById("test").innerHTML = localStorage.getItem("name1");
}

如果结果数量很少,这很快就会闪现:

results = [[lines.index(line), line] for line in lines
            if search_item in line.lower()]

“字符串预处理”就是我所说的上面的结果=操作。

这是相同的操作,但使用“1330”作为搜索项而不是“1330”。这个产生6,049个匹配而不是249个:

The search item is: [ 1330 ]
Before string pre-processing, the time is: 0.0000
The number of lines is: 1,028,952
After string pre-processing, the time is: 0.2500
The number of results is: 249

如你所见,10秒对1/4秒...此外,“1330”和“1330”搜索分别使用for循环在2.4和3.2秒内运行:

The search item is: [1330]
Before string pre-processing, the time is: 0.0000
The number of lines is: 1,028,952
After string pre-processing, the time is: 10.3180
The number of results is: 6,049

因此,对于249个结果,列表理解在性能方面提高了10倍,但对于6,049个结果则提高了3 + x ...

显然,问题不在列表理解的if / in部分(两个搜索都扫描所有1M +行并接受或拒绝每个行),而是在构建第二个“long'ish”的结果列表中案件。换句话说,瓶颈似乎在

for lineNum, line in enumerate(lines):
    if search_item in line.lower():
        return lineNum, line

部分理解。

我想我很惊讶列表理解对于大型结果集变得如此缓慢(并且6K实际上并不那么大)。我错过了什么?是否有一种我应该使用的方法,它始终优于for循环?

1 个答案:

答案 0 :(得分:4)

list.index()调用必须搜索所有行以查找匹配项。对于N行,执行O(N ^ 2)步;一条1000线变成一百万步等等。对于6k线,这是3600万步 *

如果您只需要一个行号,请使用enumerate() function生成一个:

results = [[index, line] for index, line in enumerate(lines)
            if search_item in line.lower()]

enumerate()随时添加一个运行计数器,让您的算法只执行O(N)步骤。您已在完整的for循环语句中使用此功能,但不在列表理解中。

如果您有重复行,则输出会有所不同; lines.index()找到第一个匹配,而enumerate()生成唯一的行号。

* Big-O notation为我们提供了算法的渐近行为。由于给定行 x list.index()只需要扫描(最多) x 行来查找索引,如果为每行执行此操作,则会迭代结束时,你只需要1 + 2 + 3 + ... x 步骤,这是一个triangle number。所以总共“只”((N *(N + 1))/ 2)步骤采取步骤,恰好是1/2 N ^ 2步。但是当N趋于无穷大时,乘数不再重要,最终得到O(N ^ 2)。