假设我有两个列表,l1和l2。我想执行l1 - l2,它返回l1的所有元素,而不是l2中的元素。
我可以想到一个简单的循环方法来实现这一点,但这将是非常低效的。做这件事的方法是什么?
例如,如果我有l1 = [1,2,6,8] and l2 = [2,3,5,8],l1 - l2应该返回[1,6]。
只是一个提示:pep8声明不应该使用小写"l",因为它看起来太像1。
Python有一个称为列表理解的语言特性,非常适合让这类事情变得非常简单。以下语句完全满足您的需要,并将结果存储在l3中:
l3 = [x for x in l1 if x not in l2]
l3将包含[1, 6]。
希望这有帮助!
很像Python,我喜欢!它的效率如何?
我相信这是非常有效的,而且它的好处是非常易读和清楚地知道你想要完成什么。我看到一篇关于效率的博客文章,你可能会发现它很有趣:blog.cdleary.com/2010/04/efficiency-of-list-understructions
@fandom:列表理解本身是非常有效的(虽然生成器理解可能通过不在内存中复制元素而更有效),但是in操作符在列表上没有那么有效。列表中的in是o(n),而集合中的in是o(1)。然而,在你得到成千上万个元素或者更多之前,你不太可能注意到它们之间的区别。
l3 = [x for x in l1 if x not in set(l2)]?我敢肯定,是否会不止一次地打电话给埃多克斯。
@丹诺苏尔:是的,对于l1的每个元素都会调用set(l2)。一般来说,如果你要经常使用in,你可能不应该首先使用一个列表(或tuple)(尽管如果你知道它总是很小的话,那就不可怕了)。
对于需要集合的O(1)数而不想一次又一次调用set的大列表,可以使用[x for l2s in [set(l2)] for x in l1 if x not in l2s]。
你也可以设置l2s = set(l2),然后说l3 = [x for x in l1 if x not in l2s]。稍微容易一点。
如果你能使用set,你应该使用arkku的答案。它既高效又可读。我无法在应用程序中使用集合,因为我的项目不可哈希。
仅供参考,在l3 = [x for x in l1 if x not in set(l2)]中…set(l2)部分只运行一次,因此这应该是最有效、可读的答案,不会删除重复项或重新排序列表。
@EricLindauer:如果用print(x)替换x not in set(l2)的话,就会看到l1的每个元素都被打印到stdout,即每次迭代都会调用print函数。同样适用于set(l2)。
谢谢,功能很好
谢谢,我半个小时来一直在自己想同样的问题,但你的答案却成功了!
我不同意我建议的编辑的拒绝原因:stackoverflow.com/review/suggested-edits/20710254我的编辑确实使答案更容易阅读,而且我的编辑编辑出来的内容是多余的。当先前的编辑尝试被拒绝时,我也提出了同样的批评,我的评论也被删除了。天哪!如果你必须拒绝编辑,评论家,但让我的批评站在一边。
我的首选答案。
一种方法是使用集合:
>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])
这也将从l1中删除重复项,这可能是不希望的副作用。
…并失去元素顺序(如果顺序很重要)。
对不起,帮派,这是最好的答案,也是最简单的读物。我需要生成随机密钥,并将其存储在列表中。我要确保它们是独一无二的。所以我有一个从1到10000的范围,然后我有一个我生成的数字列表。一个很好的解决方案如下:random.sample(set(range(1,10))-set([2,3]),1)
@如果你的物品是可以散列的,那么Tereuscott是最好的答案。在我的例子中,它们不是,所以我必须使用选定的答案。
需要注意的是,集合的输出是有序的,即1,3,2变为1,2,3和A、C、B变为A、B、C并且您可能不想这样做。
扩展到油炸圈饼的答案和其他答案,通过使用生成器理解而不是列表理解,以及使用set数据结构(因为in运算符在列表上是O(n),而在集合上是O(1),可以获得更好的结果。
这里有一个对你有用的函数:
def filter_list(full_list, excludes):
s = set(excludes)
return (x for x in full_list if x not in s)
结果将是一个iterable,它将懒惰地获取筛选列表。如果您需要一个真正的列表对象(例如,如果您需要对结果执行EDOCX1[5]),那么您可以轻松地构建这样的列表:
filtered_list = list(filter_list(full_list, excludes))
使用python集类型。那将是最严重的Python。:)
而且,由于它是本地的,所以它也应该是最优化的方法。
见:
http://docs.python.org/library/stdtypes.html设置
http://docs.python.org/library/sets.htm(对于旧版python)
# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2
在使用集合时,应注意的是,的输出是有序的,即1,3,2变为1,2,3和A、C、B变为A、B、C,您可能不想这样做。
如果列表l1包含重复元素,此方法将不起作用。
作为一种替代方法,您也可以将filter与lambda表达式一起使用,以获得所需的结果。例如:
>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])
# v `filter` returns the a iterator object. Here I'm type-casting
# v it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]
性能比较
这里我比较一下这里提到的所有答案的性能。正如预期的那样,Arkku基于set的运营速度最快。
Arkku的设置差异-第一个(每个循环0.124 usec)
mquadri$ python -m timeit -s"l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);""l1 - l2"
10000000 loops, best of 3: 0.124 usec per loop
Daniel Pryden对set查找的列表理解-秒(每个循环0.302 usec)
mquadri$ python -m timeit -s"l1 = [1,2,6,8]; l2 = set([2,3,5,8]);""[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.302 usec per loop
甜甜圈的清单理解在普通清单-第三个(0.552个使用循环)
mquadri$ python -m timeit -s"l1 = [1,2,6,8]; l2 = [2,3,5,8];""[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.552 usec per loop
Moinuddin Quadri使用filter四分之一(每个循环0.972 usec)
mquadri$ python -m timeit -s"l1 = [1,2,6,8]; l2 = set([2,3,5,8]);""filter(lambda x: x not in l2, l1)"
1000000 loops, best of 3: 0.972 usec per loop
Akshay Hazari使用reduce+filter-第五个(每个回路3.97 usec)
mquadri$ python -m timeit"l1 = [1,2,6,8]; l2 = [2,3,5,8];""reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
100000 loops, best of 3: 3.97 usec per loop
ps:set不维护顺序,从列表中删除重复元素。因此,如果您需要其中任何一个,请不要使用设置差异。
尽管reduce和map在计算出您想要的输出时更加灵活。其他一切都比它强,真是糟透了。
替代解决方案:
reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])
使用这种方法有什么好处吗?它看起来更复杂,更难阅读,没有太多好处。
这似乎很复杂。reduce非常灵活,可以用于很多目的。它被称为褶皱。还原实际上是折叠的。假设您想在函数中添加更复杂的内容,那么在这个函数中是可能的,但是列表理解(选择的最佳答案)只会获得相同类型的输出,即列表,可能具有相同的长度,而使用折叠,您也可以更改输出类型。en.wikipedia.org/wiki/fold_28高阶_函数%29。此解决方案的复杂性为n*m或更低。不过,其他人可能会更好,也可能不会更好。
减少(功能、列表、初始累加器(可以是任何类型)