假设我有两个列表,l1并且l2.我想执行l1 - l2,返回所有l1不在的元素l2.
我可以想到一个简单的循环方法来做到这一点,但这将是非常低效的.什么是pythonic和有效的方法呢?
举个例子,如果我有l1 = [1,2,6,8] and l2 = [2,3,5,8],l1 - l2应该回来[1,6]
Don*_*nut 418
Python有一个名为List Comprehensions的语言功能,非常适合使这类事情变得非常简单.以下语句完全符合您的要求并将结果存储在l3:
l3 = [x for x in l1 if x not in l2]
Run Code Online (Sandbox Code Playgroud)
l3将包含[1, 6].
希望这可以帮助!
Ark*_*kku 131
一种方法是使用集合:
>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])
Run Code Online (Sandbox Code Playgroud)
Dan*_*den 32
扩展Donut的答案和其他答案,通过使用生成器理解而不是列表理解,并通过使用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)
Run Code Online (Sandbox Code Playgroud)
结果将是一个可以延迟获取过滤列表的迭代.如果你需要一个真实的列表对象(例如,如果你需要对len()结果做一个),那么你可以轻松地建立一个如下列表:
filtered_list = list(filter_list(full_list, excludes))
Run Code Online (Sandbox Code Playgroud)
Moi*_*dri 30
作为替代方案,您还可以使用filterlambda表达式来获得所需的结果.例如:
>>> 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]
Run Code Online (Sandbox Code Playgroud)
绩效比较
在这里,我将比较这里提到的所有答案的表现.正如所料,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
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)甜甜圈列表理解清单 - 第三(每循环0.552 usec)
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
Run Code Online (Sandbox Code Playgroud)Moinuddin Quadri使用filter - 第四(每循环0.972次使用)
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
Run Code Online (Sandbox Code Playgroud)Akshay Hazari使用reduce+filter - Fifth的组合(每循环3.97次使用)
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
Run Code Online (Sandbox Code Playgroud)PS: set不维护订单并从列表中删除重复的元素.因此,如果您需要任何这些,请不要使用设置差异.
non*_*ot1 28
使用Python集类型.这将是最Pythonic.:)
此外,由于它是原生的,它也应该是最优化的方法.
看到:
http://docs.python.org/library/stdtypes.html#set
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
Run Code Online (Sandbox Code Playgroud)
lbs*_*eek 12
使用 Set Comprehensions {x for x in l2} 或 set(l2) 获取集合,然后使用List Comprehensions获取列表
l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]
Run Code Online (Sandbox Code Playgroud)
基准测试代码:
import time
l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))
l2set = {x for x in l2}
tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)
tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)
print("speedup %fx"%(difflist/diffset))
Run Code Online (Sandbox Code Playgroud)
基准测试结果:
0.0015058517456054688
3.968189239501953
speedup 2635.179227x
Run Code Online (Sandbox Code Playgroud)
替代解决方案:
reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])
Run Code Online (Sandbox Code Playgroud)
set.difference():您可以使用set.difference()该集合中不存在于其他集合中的元素来获取新集合。ie将返回包含 中存在但不存在于 中的set(A).difference(B)项目的集合。例如:AB
>>> set([1,2,6,8]).difference([2,3,5,8])
{1, 6}
Run Code Online (Sandbox Code Playgroud)
这是Arkku 的答案中提到的一种获得set差异的函数方法(使用算术减法运算符来计算集合差异)。 -
由于集合是无序的,因此您将失去初始列表中元素的顺序。(如果你想保持元素的顺序,请继续阅读下一节)
set基于查找如果您想保持初始 list 的顺序,那么Donut 的基于列表理解的答案就可以解决问题。但是,通过在内部使用来检查元素是否存在于其他列表中,您可以从接受的答案中获得更好的性能。例如:set
l1, l2 = [1,2,6,8], [2,3,5,8]
s2 = set(l2) # Type-cast `l2` to `set`
l3 = [x for x in l1 if x not in s2]
# ^ Doing membership checking on `set` s2
Run Code Online (Sandbox Code Playgroud)
如果您有兴趣了解为什么成员资格检查比set列表更快list,请阅读以下内容:是什么让集合比列表更快?
filter()lambda表达式这是使用lambda 表达式filter()的另一种替代方法。在这里添加它仅供参考,但性能效率不高:
>>> 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]
Run Code Online (Sandbox Code Playgroud)
filterfalse 不使用lambda 表达式当使用类似filter或filterfalse类似的函数时itertools,通常可以通过避免lambda- 表达式并使用已经存在的函数来节省性能。list和的实例set定义了__contains__用于遏制检查的方法。-运算in符在后台调用此方法,因此x in l2可以将 using 替换为l2.__contains__(x)。通常这种替换并不是真的更漂亮,但在这种特定情况下,与使用 - 表达式lambda结合使用时,它使我们能够获得更好的性能filterfalse:
>>> from itertools import filterfalse
>>> l1 = [1, 2, 6, 8]
>>> l2 = [2, 3, 5, 8]
>>> list(filterfalse(l2.__contains__, l1))
[1, 6]
Run Code Online (Sandbox Code Playgroud)
filterfalsefalse创建一个迭代器,生成用作 的参数时返回的所有元素l2.__contains__。
Sets 的实现速度更快,__contains__所以更好的是:
>>> from itertools import filterfalse
>>> l1 = [1, 2, 6, 8]
>>> l2 = set([2, 3, 5, 8])
>>> list(filterfalse(l2.__contains__, l1))
[1, 6]
Run Code Online (Sandbox Code Playgroud)
使用列表:
$ python3 -m timeit -s "from itertools import filterfalse; l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(filterfalse(l2.__contains__, l1))"
500000 loops, best of 5: 522 nsec per loop
Run Code Online (Sandbox Code Playgroud)
使用集:
$ python3 -m timeit -s "from itertools import filterfalse; l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(filterfalse(l2.__contains__, l1))"
1000000 loops, best of 5: 359 nsec per loop
Run Code Online (Sandbox Code Playgroud)