Python - 从列表中删除项目

时间:2010-10-16 04:16:08

标签: python list-comprehension

# I have 3 lists:
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
L2 = [4, 7, 8]
L3 = [5, 2, 9]
# I want to create another that is L1 minus L2's memebers and L3's memebers, so:
L4 = (L1 - L2) - L3  # Of course this isn't going to work

我想知道,做到这一点的“正确”方法是什么。我可以用很多不同的方式来做,但Python的风格指南说应该只有一种正确的方法来做每件事。我从来不知道这是什么。

6 个答案:

答案 0 :(得分:10)

以下是一些尝试:

L4 = [ n for n in L1 if (n not in L2) and (n not in L3) ]  # parens for clarity

tmpset = set( L2 + L3 )
L4 = [ n for n in L1 if n not in tmpset ]

现在我有一点时间思考,我意识到L2 + L3事件会创建一个临时列表,立即被抛弃。所以更好的方法是:

tmpset = set(L2)
tmpset.update(L3)
L4 = [ n for n in L1 if n not in tmpset ]

更新:我看到一些关于性能的奢侈声明,我想声称我的解决方案已经尽可能快。创建中间结果,无论它们是中间列表还是中间迭代器,然后必须重复调用,总是会比简单地给L2L3让集合像我一样直接迭代一样慢在这里做了。

$ python -m timeit \
  -s 'L1=range(300);L2=range(30,70,2);L3=range(120,220,2)' \
  'ts = set(L2); ts.update(L3); L4 = [ n for n in L1 if n not in ts ]'
10000 loops, best of 3: 39.7 usec per loop

所有其他替代方案(我能想到的)必然比这慢。例如,自己做循环而不是让set()构造函数执行它们会增加费用:

$ python -m timeit \
  -s 'L1=range(300);L2=range(30,70,2);L3=range(120,220,2)' \
  'unwanted = frozenset(item for lst in (L2, L3) for item in lst); L4 = [ n for n in L1 if n not in unwanted ]'
10000 loops, best of 3: 46.4 usec per loop

使用迭代器,它们涉及的所有状态保存和回调显然会更加昂贵:

$ python -m timeit \
  -s 'L1=range(300);L2=range(30,70,2);L3=range(120,220,2);from itertools import ifilterfalse, chain' \
  'L4 = list(ifilterfalse(frozenset(chain(L2, L3)).__contains__, L1))' 
10000 loops, best of 3: 47.1 usec per loop

所以我相信我昨晚给出的答案仍然很遥远(对于“远远超过”大约5微秒的值,显然)是最好的,除非提问者在L1中有重复和每次重复出现在其他一个列表中时,都希望将它们删除一次。

答案 1 :(得分:6)

答案 2 :(得分:0)

假设您的个人列表不包含重复项....请使用SetDifference

L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
L2 = [4, 7, 8]
L3 = [5, 2, 9]
print(list(set(L1) - set(L2) - set(L3)))

答案 3 :(得分:0)

在列表中执行此类操作可能会很快妨碍您的程序性能。每次删除都会发生什么,List操作会做一个新的malloc&移动元素。如果你有一个非常庞大的列表或其他,这可能是昂贵的。所以我建议这个 -

我假设你的清单有独特的元素。否则,您需要在dict中维护一个具有重复值的列表。无论如何,对于您提供的数据,这里是 -

方法1

d = dict()
for x in L1: d[x] = True

# Check if L2 data is in 'd'
for x in L2:
    if x in d:
        d[x] = False

for x in L3:
    if x in d:
        d[x] = False

# Finally retrieve all keys with value as True.
final_list = [x for x in d if d[x]]

方法2 如果所有看起来像太多的代码。然后你可以尝试使用set。但是这样你的列表就会丢失所有重复的元素。

final_set  = set.difference(set(L1),set(L2),set(L3))
final_list = list(final_set)

答案 4 :(得分:0)

这可能比列表理解答案更少pythonesque,但更简单:

l1 = [ ... ]
l2 = [ ... ]

diff = list(l1) # this copies the list
for element in l2:
    diff.remove(element)

这里的优势在于我们保留了列表的顺序,如果有重复元素,我们每次在l2中显示只删除一个。

答案 5 :(得分:0)

我认为直觉的答案对于这样一个简单的问题来说太长了,Python已经有了一个内置函数来将两个列表链接为一个生成器。

程序如下:

  1. 使用itertools.chain链接L2和L3,而不创建占用大量内存的副本
  2. 从中创建一个集合(在这种情况下,冻结集会执行,因为我们在创建后不会更改它)
  3. 使用列表推导来过滤掉L1和L2或L3中的元素。由于set / frozenset lookup(x in someset)是O(1),因此速度非常快。
  4. 现在代码:

    L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    L2 = [4, 7, 8]
    L3 = [5, 2, 9]
    
    from itertools import chain
    tmp = frozenset(chain(L2, L3))
    L4 = [x for x in L1 if x not in tmp] # [1, 3, 6]
    

    这应该是最快,最简单,耗电量最少的解决方案之一。