繁体   English   中英

迭代特定列表的最快方法?

[英]Fastest possible way to iterate through a specific list?

假设我有一个清单:

list=['plu;ean;price;quantity','plu1;ean1;price1;quantity1']

我想迭代列表+用“;”拆分列表 并添加一个if子句,如下所示:

for item in list:
    split_item=item.split(";")
    if split_item[0] == "string_value" or split_item[1] == "string_value":
        do something.....

我想知道,如果这是最快的方法吗? 假设我的初始列表更大(包含更多列表项)。 我尝试了列表推导:

item=[item.split(";") for item in list if item.split(";")[0] == "string_value" or item.split(";")[1] == "string_value"]

但这实际上给了我较慢的结果。 第一种情况是给我平均90ms,而第二种情况给我平均130ms。 我做列表理解错了吗? 有更快的解决方案吗?

我想知道,如果这是最快的方法吗?

不,当然不。 您可以在手动编码的程序集中比在Python中更快地实现它。 所以呢?

如果“做某事......”并不是微不足道的,并且有很多匹配,那么做100000次的成本要比循环500000次的成本贵很多,所以找到最快的循环方式什么都不重要。

事实上,只是调用每个循环split两到三个而不是记住和重用结果将会淹没迭代的成本,而当你只关心两个结果时也不会传递一个maxsplit参数。


所以,你正试图优化错误的东西。 但是,如果在你解决了其他问题之后,事实证明迭代的成本在这里确实很重要呢?

好吧,你不能直接使用理解来加快速度,因为理解是针对返回值的表达式,而不是用于做事情的语句。

但是,如果你看看你的代码,你会发现你实际上做了三件事:分割每个字符串,然后过滤掉那些不匹配的字符串,然后做“做某事”。 因此,您可以使用前两个部分的理解,然后您只使用缓慢的for循环来获得通过过滤器的小得多的值列表。

看起来你试过这个,但是你犯了两个错误。

首先,你最好使用生成器表达式而不是列表理解 - 你不需要这里的列表,只需要迭代的东西,所以不要付钱来构建一个。

其次,您不希望将字符串split三次。 你可以找到一些令人费解的方法,在一次理解中完成一次split ,但为什么要费心呢? 只需将每个步骤写为自己的步骤。

所以:

split_items = (item.split(';') for item in items)
filtered_items = (item for item in split_items 
                  if item[0] == "string_value" or item[1] == "string_value")
for item in filtered_items:
    do something...

这实际上会更快吗? 如果您可以获得一些真实的测试数据,并且“做某事......”代码,这表明迭代是一个瓶颈,您可以测试该真实数据和代码。 在那之前,没有什么可以测试的。

仅当从str.split(';', 2)检索的前两项满足条件时才拆分整个字符串:

>>> strs = 'plu;ean;price;quantity'
>>> strs.split(';', 2)
['plu', 'ean', 'price;quantity']

只有当前两个项满足条件时,才会拆分第三个项目( 'price;quantity' ):

>>> lis = ['plu;ean;price;quantity'*1000, 'plu1;ean1;price1;quantity1'*1000]*1000

正常for循环,单个分割整个字符串列表的每个项目。

>>> %%timeit
for item in lis:
    split_item=item.split(";")
    if split_item[0] == "plu" or split_item[1] == "ean":pass
... 
1 loops, best of 3: 952 ms per loop

列表理解等效于上面的for循环:

>>> %timeit [x for x in (item.split(';') for item in lis) if x[0]== "plu" or x[1]=="ean"]
1 loops, best of 3: 961 ms per loop

按需拆分:

>>> %timeit [[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== "plu" or y=="ean"]
1 loops, best of 3: 508 ms per loop

当然,如果列表和字符串很小,那么这种优化并不重要。

我在这里找到了一个很好的选择

您可以使用地图和过滤器的组合。 试试这个:

>>>import itertools
>>>splited_list = itertools.imap(lambda x: x.split(";"), your_list)
>>>result = filter(lambda x: filter(lambda x: x[0] == "plu" or x[1] == "string_value", lista)

第一项将创建元素的迭代器。 而第二个将过滤它。 我在我的IPython Notebook shell中运行了一个小基准测试,得到了以下结果:

第一次测试:

在此输入图像描述

小尺寸,单线解决方案效果更好

第二次测试:

在此输入图像描述

使用更大的列表,地图/过滤器解决方案稍微好一些

第3次测试:

在此输入图像描述

凭借大清单和更大的元素,地图/过滤器解决方案更好。

我认为随着列表的大小逐渐增加,性能上的差异会继续增加,直到66%的时间达到峰值(在10000元素列表试验中)。

map / filter解决方案和列表推导解决方案之间的区别在于.split()的调用次数。 Ones为每个项目调用它3次,另一个只调用一次,因为列表推导只是一种pythonic方式来一起进行map / filter。 我过去经常使用列表推导,并认为我不知道lambda是什么。 直到我发现地图和列表推导是一回事。

如果您不关心内存使用情况,可以使用常规地图而不是imap。 它将立即创建包含拆分的列表。 它会使用更多内存来存储它,但速度稍快。

实际上,如果您不关心内存使用情况,可以使用2个列表推导来编写地图/过滤器解决方案,并获得相同的结果。 查看:

在此输入图像描述

编辑:事实证明,正则表达式缓存对竞争有点不公平。 我的错。 正则表达式只有一小部分更快。

如果你正在寻找速度,hcwhsa的答案应该足够好。 如果您需要稍多,看向re

import re
from itertools import chain

lis = ['plu;ean;price;quantity'*1000, 'plu1;ean1;price1;quantity1'*100]*1000

matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match
[l.split(';') for l in lis if matcher(l)]

计时,主要是积极的结果(又称split是缓慢的主要原因):

SETUP="
import re
from itertools import chain
matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match

lis = ['plu1;ean1;price1;quantity1'+chr(i) for i in range(10000)] + ['plu;ean;price;quantity' for i in range(10000)]
"

python -m timeit -s "$SETUP" "[[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean']"
python -m timeit -s "$SETUP" "[l.split(';') for l in lis if matcher(l)]"

我们看到我的快一点。

10 loops, best of 3: 55 msec per loop
10 loops, best of 3: 49.5 msec per loop

对于大多数负面结果(大多数事情被过滤):

SETUP="
import re
from itertools import chain
matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match

lis = ['plu1;ean1;price1;quantity1'+chr(i) for i in range(1000)] + ['plu;ean;price;quantity' for i in range(10000)]
"

python -m timeit -s "$SETUP" "[[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean']"
python -m timeit -s "$SETUP" "[l.split(';') for l in lis if matcher(l)]"

铅是一种触摸高。

10 loops, best of 3: 40.9 msec per loop
10 loops, best of 3: 35.7 msec per loop

如果结果始终是唯一的,请使用

next([x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean')

或更快的正则表达式版本

next(filter(matcher, lis)).split(';')

(在Python 2上使用itertools.ifilter )。

时序:

SETUP="
import re
from itertools import chain
matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match

lis = ['plu1;ean1;price1;quantity1'+chr(i) for i in range(10000)] + ['plu;ean;price;quantity'] + ['plu1;ean1;price1;quantity1'+chr(i) for i in range(10000)]
"

python -m timeit -s "$SETUP" "[[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean']"
python -m timeit -s "$SETUP" "next([x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean')"

python -m timeit -s "$SETUP" "[l.split(';') for l in lis if matcher(l)]"
python -m timeit -s "$SETUP" "next(filter(matcher, lis)).split(';')"

结果:

10 loops, best of 3: 31.3 msec per loop
100 loops, best of 3: 15.2 msec per loop
10 loops, best of 3: 28.8 msec per loop
100 loops, best of 3: 14.1 msec per loop

因此,这对两种方法都有很大的推动作用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM