Python 优化盈亏平衡点:在集合上迭代多次还是先转换为列表?
这是我一直想知道的事情。我将向Python提出这个问题,但我也欢迎回答爪哇和C++标准库中的答案。 假设您有一个名为“my_list”的Python列表,您希望迭代它的唯一的元素。有两种自然方法:Python 优化盈亏平衡点:在集合上迭代多次还是先转换为列表?,python,optimization,hash,list,iterator,Python,Optimization,Hash,List,Iterator,这是我一直想知道的事情。我将向Python提出这个问题,但我也欢迎回答爪哇和C++标准库中的答案。 假设您有一个名为“my_list”的Python列表,您希望迭代它的唯一的元素。有两种自然方法: #iterate over set for x in set(my_list): do_something(x) 或 紧张的是,在列表上迭代要比在集合上迭代快,但将集合转换为列表需要时间。我猜这个问题的答案将取决于许多因素,例如: 我们需要迭代多少次 原始列表有多大 原始列表中应该重复多少
#iterate over set
for x in set(my_list):
do_something(x)
或
紧张的是,在列表上迭代要比在集合上迭代快,但将集合转换为列表需要时间。我猜这个问题的答案将取决于许多因素,例如:
- 我们需要迭代多少次
- 原始列表有多大
- 原始列表中应该重复多少次
import collections
import timeit
blackhole = collections.deque(maxlen=0).extend
s = set(xrange(10000))
我们看到,对于较大的n
,越简单越好:
>>> timeit.timeit(lambda: blackhole(s))
108.87403416633606
>>> timeit.timeit(lambda: blackhole(list(s)))
189.0135440826416
对于较小的n
而言,相同的关系成立:
>>> s = set(xrange(10))
>>> timeit.timeit(lambda: blackhole(s))
0.2969839572906494
>>> timeit.timeit(lambda: blackhole(list(s)))
0.630713939666748
是的,列表的迭代速度比集合快(在您自己的Python解释器上尝试):
但这并不意味着您应该将集合转换为列表以进行迭代
盈亏平衡分析
好的,您已经分析了您的代码,并且发现您在一个集合上迭代了很多次(我假设这个集合是静态的,否则我们所做的是非常有问题的)。我希望您熟悉set方法,而不是复制该功能。(我也认为你应该考虑把FuffeSSET和元组联系起来,因为使用一个列表(可变的)来代替一个规范的集合(也是可变的)似乎是容易出错的。)用这个警告,让我们做一个分析。
这可能是因为,通过投资复杂性和承担更多代码行错误的更大风险,您可以获得良好的回报。该分析将证明这方面的盈亏平衡点。我不知道您需要为更大的风险和开发时间付出多大的性能代价,但这将告诉您在什么时候可以开始为这些付出代价:
import collections
import timeit
import pandas as pd
BLACKHOLE = collections.deque(maxlen=0).extend
SET = set(range(1000000))
def iterate(n, iterable):
for _ in range(n):
BLACKHOLE(iterable)
def list_iterate(n, iterable):
l = list(iterable)
for _ in range(n):
BLACKHOLE(l)
columns = ('ConvertList', 'n', 'seconds')
def main():
results = {c: [] for c in columns}
for n in range(21):
for fn in (iterate, list_iterate):
time = min(timeit.repeat((lambda: fn(n, SET)), number=10))
results['ConvertList'].append(fn.__name__)
results['n'].append(n)
results['seconds'].append(time)
df = pd.DataFrame(results)
df2 = df.pivot('n', 'ConvertList')
df2.plot()
import pylab
pylab.show()
看起来你的盈亏平衡点是在5次完整的迭代中。平均5个或更少,这样做是没有意义的。只有在5行或更多行代码时,您才开始补偿额外的开发时间、复杂性(增加的维护成本)和更多行代码带来的风险
我认为你必须做很多,才能在项目中增加复杂性和代码行
这些结果是使用Anaconda的Python 2.7从Ubuntu 14.04终端创建的。对于不同的实现和版本,您可能会得到不同的结果
担心 我关心的是集合是可变的,而列表是可变的。集合将阻止您在迭代时对其进行修改,但从该集合创建的列表不会:
>>> s = set('abc')
>>> for e in s:
... s.add(e + e.upper())
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Set changed size during iteration
这就是为什么我还建议使用冻结集和元组。它将是一个内置的保护,防止对数据进行语义错误的更改
>>> s = frozenset('abc')
>>> t_s = tuple(s)
>>> for e in t_s:
... s.add(e + e.upper())
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
>s=frozenset('abc'))
>>>t_s=元组(s)
>>>对于e in t_s:
... s、 添加(e+e.upper())
...
回溯(最近一次呼叫最后一次):
文件“”,第2行,在
AttributeError:“frozenset”对象没有属性“add”
最后,你必须相信自己,才能使你的算法正确。经常有人告诉我,当我警告新的Python用户这类事情时,我给出了很好的建议。他们知道这是一个很好的建议,因为一开始他们没有倾听,发现这会造成不必要的复杂性、复杂性和由此产生的问题。但也有一些事情,比如逻辑正确性,如果你做得不对,你只能怪你自己。将可能出错的事情最小化是一个好处,通常值得进行性能权衡。
同样,如果性能(而不是开发的正确性或速度)是解决此项目的首要问题,那么您就不会使用Python。1在Python中,因为迭代器是如何实现的。一般来说,这可能取决于,如果语言没有迭代器的概念,迭代器可以保留它们在迭代序列中的位置,那么最好事先显式地创建一个向量,但是很难想象在哪种语言中会发生这种情况。可能是JavaScript之类的随机不合逻辑的东西?Paul,谢谢你给我提供了回答这个问题的机会,我希望这会有所帮助,如果有任何部分不清楚,请告诉我,我会澄清。@kdgregory python集是一个隐藏的哈希表,对哈希表的迭代与元素的数量加上桶的数量成正比;对于列表来说,这只是元素的数量。在哈希表上迭代可能是O(条目+存储桶),但由于存储桶计数(通常)与条目数量成比例,这相当于O(条目),即,它具有与在典型列表结构上迭代相同的算法复杂性。相对速度下降到特定的i
>>> s = set('abc')
>>> for e in s:
... s.add(e + e.upper())
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Set changed size during iteration
>>> for e in list(s):
... s.add(e + e.upper())
>>> s = frozenset('abc')
>>> t_s = tuple(s)
>>> for e in t_s:
... s.add(e + e.upper())
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'frozenset' object has no attribute 'add'