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

Python 迭代特定列表的最快方法?,python,performance,list,list-comprehension,Python,Performance,List,List Comprehension,假设我有一个列表: 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..

假设我有一个列表:

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"]
但这实际上给了我更慢的结果。第一个案例的平均时间是90毫秒,而第二个案例的平均时间是130毫秒。
我做的清单错了吗?是否有更快的解决方案?

仅当从
str.Split(“;”,2)
检索到的前两项满足以下条件时,才拆分整个字符串:

>>> strs = 'plu;ean;price;quantity'
>>> strs.split(';', 2)
['plu', 'ean', '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
当然,如果列表和字符串很小,那么这种优化就无关紧要了

我在想,这是不是最快的办法

不,当然不是。您可以在手工编码的程序集中比在Python中更快地实现它。那又怎样

如果“做点什么…”不是一件小事,而且有很多匹配项,那么做100000次的成本将比循环500000次的成本要昂贵得多,因此找到最快的循环方式根本不重要

事实上,只需调用
split
2到3个循环,而不是记住和重用结果,将大大降低迭代的成本,并且在只关心两个结果的情况下不传递maxsplit参数也可能


所以,你试图优化错误的东西。但是,如果在修复了所有其他问题之后,迭代的成本在这里真的很重要呢

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

但是,如果您查看代码,就会发现实际上您正在做三件事:拆分每个字符串,然后过滤掉不匹配的字符串,然后执行“做点什么”。因此,您可以对前两部分进行理解,然后只对通过过滤器的较小值列表使用慢速
for
循环

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

首先,您最好使用生成器表达式,而不是列表理解。在这里,您不需要列表,只是需要迭代的内容,所以不必花钱构建列表

其次,您不希望将字符串拆分三次。您可能会找到一些复杂的方法来在一次理解中完成
拆分
,但是为什么要麻烦呢?只需将每个步骤都写为自己的步骤

因此:


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

我在这里找到了一个很好的替代方案

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

>>>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笔记本外壳中运行了一个小基准测试,得到了以下结果:

第一次测试:

对于较小的尺寸,单线解决方案效果更好

第二次测试:

列表越大,映射/过滤器解决方案就越好

第三次测试:

有了一个大的列表和更大的元素,映射/过滤解决方案就更好了

我猜随着列表的大小,性能上的差异会继续增加,直到在66%以上的时间内达到峰值(在10000元素列表的试用中)

映射/筛选器解决方案与列表理解解决方案之间的区别在于调用.split()的次数。有人对每个项目调用它3次,另一个只调用一次,因为列表理解只是一种将映射/过滤结合在一起的python方法。我过去经常使用列表理解法,认为我不知道lambda是关于什么的。直到我发现地图和列表的理解是一样的

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

实际上,如果不关心内存使用情况,可以使用两种列表理解编写映射/过滤器解决方案,并获得相同的精确结果。结帐:

EDIT:原来Regex缓存对竞争对手有点不公平。我的错。正则表达式只快了一小部分。 如果你在寻找速度,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
是缓慢的主要原因):

我们看到我的要快一点

10 loops, best of 3: 55 msec per loop
10 loops, best of 3: 49.5 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(';')
(在Python2上使用
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

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

在第一种情况下,您只需调用一次
item.split(“;”)
。在第二种情况下,调用
item.split(;”)
3次。第二种情况肯定要慢一些。旁白:如果您正在处理表格数据,那么可能值得研究类似的库。当/如果您可以对操作进行矢量化,您可以比纯Python循环获得显著的性能优势,当然这完全取决于您没有提到的问题的各个部分。我同意这就是问题所在:如果您只需要前两个元素,就不必拆分所有列表。这里的优势是
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(';')
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