在Python中从序列中删除项目的优雅方法?

时间:2020-03-05 18:41:12  来源:igfitidea点击:

当我用Python编写代码时,经常需要根据某些条件从列表或者其他序列类型中删除项目。我还没有找到一种既优雅又高效的解决方案,因为从当前正在迭代的列表中删除项目是很糟糕的。例如,我们不能执行以下操作:

for name in names:
    if name[-5:] == 'Smith':
        names.remove(name)

我通常最终会做这样的事情:

toremove = []
for name in names:
    if name[-5:] == 'Smith':
        toremove.append(name)
for name in toremove:
    names.remove(name)
del toremove

这是效率低下,相当丑陋甚至可能是错误的(它如何处理多个" John Smith"条目?)。有没有人有一个更优雅的解决方案,或者至少是一个更高效的解决方案?

与字典配合使用的那一个怎么样?

解决方案

回答

好吧,这显然是我们正在使用的数据结构的问题。例如,使用哈希表。某些实现每个键支持多个条目,因此可以将最新元素弹出,或者将其全部删除。

但这就是我们要找到的解决方案,它是通过不同的数据结构(而不​​是算法)带来的优雅。如果对它进行排序或者其他操作,也许可以做得更好,但是列表中的迭代是这里唯一的方法。

编辑:确实意识到他要求"效率" ...所有这些建议的方法都只是遍历列表,这与他的建议相同。

回答

names = filter(lambda x: x[-5:] != "Smith", names);

回答

过滤器对此将是很棒的。简单的例子:

names = ['mike', 'dave', 'jim']
filter(lambda x: x != 'mike', names)
['dave', 'jim']

编辑:Corey的列表理解也是很棒的。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

回答

使用列表理解

list = [x for x in list if x[-5:] != "smith"]

回答

仅完成过滤的两种简单方法是:

  • 使用filternames = filter(lambda name:name [-5:]!=" Smith",名称)
  • 使用列表推导:`names = [如果name [-5:]则为name中的name,如果= [Smith"]]

请注意,这两种情况都将谓词函数求值的值保留为True,因此我们必须颠倒逻辑(即我们说"保留姓氏为史密斯的人"而不是"删除姓氏为史密斯的人"姓史密斯")。

编辑好笑...两个人分别张贴了我在发布我的同时提出的两个答案。

回答

过滤和理解这两个解决方案都需要建立一个新列表。我不太了解Python的内部知识,但我认为采用更传统(但不太优雅)的方法可能会更有效:

names = ['Jones', 'Vai', 'Smith', 'Perez']

item = 0
while item <> len(names):
    name = names [item]
    if name=='Smith':
        names.remove(name)
    else:
        item += 1

print names

无论如何,对于简短列表,我坚持使用先前提出的两种解决方案中的任何一种。

回答

对于示例,可以使用filter和list理解,但是有两个问题:

  • 他们会复制列表,然后返回新列表,当原始列表很大时,效率会很低
  • 他们可真是繁琐时的标准挑选的物品(你的情况,如果name [-5:] =='史密斯)是比较复杂的,或者有几个条件。

原始解决方案实际上对于大型列表更有效,即使我们可以接受它比较丑陋。但是,如果我们担心可以有多个"约翰·史密斯",可以通过根据排名而不是根据价值进行删除来解决:

names = ['Jones', 'Vai', 'Smith', 'Perez', 'Smith']

toremove = []
for pos, name in enumerate(names):
    if name[-5:] == 'Smith':
        toremove.append(pos)
for pos in sorted(toremove, reverse=True):
    del(names[pos])

print names

我们不能在不考虑列表大小的情况下选择解决方案,但是对于大型列表,我希望我们使用两遍解决方案,而不是使用过滤器或者列表理解功能

回答

有时过滤(使用过滤器或者列表推导)不起作用。当其他某个对象持有对我们正在修改的列表的引用,而我们需要在适当的位置修改列表时,就会发生这种情况。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。.########## $!#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

for name in names[:]:
    if name[-5:] == 'Smith':
        names.remove(name)

与原始代码的唯一区别是在for循环中使用了"名称[:]"而不是"名称"。这样,代码将在列表的(浅)副本上进行迭代,删除操作将按预期进行。由于列表复制较浅,因此相当快。

回答

要回答有关使用字典的问题,我们应该注意Python 3.0将包括dict理解:

>>> {i : chr(65+i) for i in range(4)}

同时,我们可以通过以下方式进行准字典理解:

>>> dict([(i, chr(65+i)) for i in range(4)])

或者作为更直接的答案:

dict([(key, name) for key, name in some_dictionary.iteritems if name[-5:] != 'Smith'])

回答

我们还可以向后遍历列表:

for name in reversed(names):
    if name[-5:] == 'Smith':
        names.remove(name)

这样做的好处是它不会创建新列表(例如filter或者列表理解),而是使用迭代器代替列表副本(例如)。

请注意,尽管在向后迭代的同时删除元素是安全的,但插入元素有些麻烦。

回答

在一套的情况下。

toRemove = set([])  
for item in mySet:  
    if item is unwelcome:  
        toRemove.add(item)  
mySets = mySet - toRemove