我在寻找计算两个列表差异的方法时,只是阅读了另一个用户问题。
Python, compute list difference
我的问题是我为什么要这样做
def diff(a,b):
b = set(b)
return [aa for aa in a if aa not in b]
而不是
def diff(a,b):
tmp = []
for i in a:
if(i not in b):
tmp.append(i)
return tmp
编辑:刚注意到第二个diff函数实际上返回了相似之处。现在应该是正确的。
答案 0 :(得分:8)
从算法的角度来看,构造集合需要O(n)
而列表理解需要O(n)
(因为测试集合中是否包含元素是O(1)
)。但是在第二个示例中,将O(n^2)
循环遍历两个列表。所以无论编程语言如何,第一种方法都是优越的。
此外,python中的列表推导本身比for循环更快。这进一步降低了常数因子(并且也显着降低)。我可以在this post中总结其中的原因:
列表推导只能由...组成 表达,而不是陈述,是一个相当重要的因素,更少 每次迭代都需要幕后工作。另一个因素 是列表推导的基础迭代机制是 比执行for循环更接近C循环。
答案 1 :(得分:5)
这两个选项之间的主要区别在于使用set
的选项渐近效率更高。
在合理有利的条件下,查找集合中的项目可以在O(1)
时间内完成;查找列表中的项目需要O(n)
时间。
第二个不太重要的区别是,一个版本使用列表推导,而另一个版本使用for
循环。列表推导倾向于产生更紧凑的代码。它们也往往更有效率(尽管性能是一个问题,获得准确图片的唯一方法是运行基准测试)。
答案 2 :(得分:2)
列表理解通常被认为比创建列表时使用常规列表操作更有效。如果考虑内存,您可能需要使用生成器。
This提供了for
- 循环,map
和list comprehension
的一些信息。 @benhoyt还提供了一个有用的link比较循环与列表理解。
但是,请注意,如果性能是一个特定的问题,那么您可能值得花时间/基准测试各种选项,以便根据您的特定要求选择最佳选项。
答案 3 :(得分:2)
使用该集通常更快,因为它只需要迭代b
一次,而您的示例必须为b
中的每个元素迭代a
。
答案 4 :(得分:2)
我做了一些测试:
test_lists.py
a = range(1, 1000)
b = range(2, 1002)
tmp = []
for i in a:
if(i not in b):
tmp.append(i)
test_set_list_comprehensions.py
a = range(1, 1000)
b = range(2, 1002)
b = set(b)
[aa for aa in a if aa not in b]
test_set.py
a = range(1, 1000)
b = range(2, 1002)
list(set(a).difference(set(b)))
这就是timeit所说的:
~$ python -m timeit 'import test_lists'
1000000 loops, best of 3: 0.671 usec per loop
~$ python -m timeit 'import test_set_list_comprehension'
1000000 loops, best of 3: 0.766 usec per loop
~$ python -m timeit 'import test_set'
1000000 loops, best of 3: 0.656 usec per loop
所以最好的一个似乎是:
test_set.py
a = range(1, 1000)
b = range(2, 1002)
list(set(a).difference(set(b)))