从一个列表移除所有在另一个列表中出现的元素

612 投票
13 回答
544540 浏览
提问于 2025-04-16 07:15

假设我有两个列表,l1l2。我想要做的是 l1 - l2,也就是找出所有在 l1 中但不在 l2 中的元素。

我可以想到用一个简单的循环来实现这个功能,但这样做效率会很低。有没有更好、更高效的方法来做到这一点呢?

举个例子,如果我有 l1 = [1,2,6,8] 和 l2 = [2,3,5,8],那么 l1 - l2 应该返回 [1,6]

13 个回答

185

性能比较

这里比较了所有答案在 Python 3.9.1Python 2.7.16 上的性能。

Python 3.9.1

以下是按性能排序的答案:

  1. Arkkuset 差集使用减法 "-" 操作 - (每次循环 91.3 纳秒)

    mquadri$ python3 -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
    5000000 loops, best of 5: 91.3 nsec per loop
    
  2. Moinuddin Quadri使用 set().difference() - (每次循环 133 纳秒)

    mquadri$ python3 -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1.difference(l2)"
    2000000 loops, best of 5: 133 nsec per loop
    
  3. Moinuddin Quadri列表推导结合 set 查找 - (每次循环 366 纳秒)

     mquadri$ python3 -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 5: 366 nsec per loop
    
  4. Donut普通列表上的列表推导 - (每次循环 489 纳秒)

     mquadri$ python3 -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
     500000 loops, best of 5: 489 nsec per loop
    
  5. Daniel Pryden生成器表达式结合 set 查找 并转换为 list - (每次循环 583 纳秒) : 明确转换为列表以获得最终对象为 list,这是 OP 的要求。如果用 列表推导 替换 生成器表达式,结果将与 Moinuddin Quadri 的列表推导结合 set 查找相同。

     mquadri$ mquadri$ python3 -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(x for x in l1 if x not in l2)"
     500000 loops, best of 5: 583 nsec per loop
    
  6. Moinuddin Quadri使用 filter() 并明确转换为 list (需要明确转换,因为在 Python 3.x 中,它返回迭代器) - (每次循环 681 纳秒)

     mquadri$ python3 -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(filter(lambda x: x not in l2, l1))"
     500000 loops, best of 5: 681 nsec per loop
    
  7. Akshay Hazari结合 functools.reduce + filter - (每次循环 3.36 微秒) : 明确转换为 list,因为从 Python 3.x 开始它返回迭代器。还需要导入 functools 才能在 Python 3.x 中使用 reduce

     mquadri$ python3 -m timeit "from functools import reduce; l1 = [1,2,6,8]; l2 = [2,3,5,8];" "list(reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2))"
     100000 loops, best of 5: 3.36 usec per loop
    

Python 2.7.16

以下是按性能排序的答案:

  1. Arkkuset 差集使用减法 "-" 操作 - (每次循环 0.0783 微秒)

    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.0783 usec per loop
    
  2. Moinuddin Quadri使用 set().difference() - (每次循环 0.117 微秒)

    mquadri$ mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1.difference(l2)"
    10000000 loops, best of 3: 0.117 usec per loop
    
  3. Moinuddin Quadri列表推导结合 set 查找 - (每次循环 0.246 微秒)

     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.246 usec per loop
    
  4. Donut普通列表上的列表推导 - (每次循环 0.372 微秒)

     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.372 usec per loop
    
  5. Moinuddin Quadri使用 filter() - (每次循环 0.593 微秒)

     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.593 usec per loop
    
  6. Daniel Pryden生成器表达式结合 set 查找 并转换为 list - (每次循环 0.964 微秒) : 明确转换为列表以获得最终对象为 list,这是 OP 的要求。如果用 生成器表达式 替换 列表推导,结果将与 Moinuddin Quadri 的列表推导结合 set 查找相同。

     mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(x for x in l1 if x not in l2)"
     1000000 loops, best of 3: 0.964 usec per loop
    
  7. Akshay Hazari结合 functools.reduce + filter - (每次循环 2.78 微秒)

     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: 2.78 usec per loop
    
248

一种方法是使用集合:

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])

不过要注意,集合不保留元素的顺序,而且会自动去掉重复的元素。此外,集合里的元素必须是可以被哈希的。如果这些限制可以接受,那么使用集合通常是最简单且性能最高的选择。

780

Python 有一个叫做 列表推导式 的功能,非常适合用来轻松处理这种情况。下面这行代码正好能实现你想要的效果,并把结果存储在 l3 里:

l3 = [x for x in l1 if x not in l2]

l3 的内容将会是 [1, 6]

撰写回答