Python:从列表中删除字典

时间:2009-08-05 20:41:14

标签: python list dictionary

如果我有一个词典列表,请说:

[{'id': 1, 'name': 'paul'},
{'id': 2, 'name': 'john'}]

我想删除id为2(或名称john)的字典,以编程方式进行此操作的最有效方法是什么(也就是说,我不知道索引是什么列表中的条目,因此不能简单地弹出)。

7 个答案:

答案 0 :(得分:92)

thelist[:] = [d for d in thelist if d.get('id') != 2]

编辑:由于在对此代码的性能的评论中表达了一些疑问(一些基于对Python的性能特征的误解,一些假设超出给定的规范,假设只有一个dict in关键字'id'的值为2的列表,我希望在这一点上保证。

在旧的Linux机器上,测量此代码:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 82.3 usec per loop

其中大约57微秒为random.shuffle(需要确保要删除的元素不总是在同一点;-)和0.65微秒的初始副本(谁担心Python的浅拷贝的性能影响)列表最明显是午餐;-),需要避免改变循环中的原始列表(因此循环的每一段都有删除的东西; - )。

如果知道只有一个要删除的项目,则可以更加迅速地找到并删除它:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(99)]; import random" "thelist=list(lod); random.shuffle(thelist); where=(i for i,d in enumerate(thelist) if d.get('id')==2).next(); del thelist[where]"
10000 loops, best of 3: 72.8 usec per loop

(如果您使用的是Python 2.6或更高版本,请使用next内置而不是.next方法 - 但如果满足删除的dicts数量,则此代码会中断条件不完全是一个。推广这一点,我们有:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
10000 loops, best of 3: 23.7 usec per loop
如我们所知,可以删除洗牌,因为已经有三个等间隔的序列要删除。 listcomp没有变化,表现良好:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*3; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
10000 loops, best of 3: 23.8 usec per loop

完全颈部和颈部,甚至只有3个99元素被移除。随着更长的列表和更多的重复,这当然更多:

$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); where=[i for i,d in enumerate(thelist) if d.get('id')==2]; where.reverse()" "for i in where: del thelist[i]"
1000 loops, best of 3: 1.11 msec per loop
$ python -mtimeit -s"lod=[{'id':i, 'name':'nam%s'%i} for i in range(33)]*133; import random" "thelist=list(lod); thelist[:] = [d for d in thelist if d.get('id') != 2]"
1000 loops, best of 3: 998 usec per loop

总而言之,显然不值得部署制作和撤销要删除的索引列表的微妙之处,与完全简单明了的列表理解相比,在一个小案例中可能获得100纳秒 - 并且在以下情况下损失113微秒一个更大的;-)。避免或批评简单,直接和完美的性能适当的解决方案(例如列表理解这一类“从列表中移除一些项目”的问题)是Knuth和Hoare着名论文的一个特别令人讨厌的例子,即“过早优化是编程中所有邪恶的根源“! - )

答案 1 :(得分:8)

这是一种使用列表理解的方法(假设你将列表命名为'foo'):

[x for x in foo if not (2 == x.get('id'))]

替换'john' == x.get('name')或其他适当的内容。

filter也有效:

foo.filter(lambda x: x.get('id')!=2, foo)

如果你想要一个生成器,你可以使用itertools:

itertools.ifilter(lambda x: x.get('id')!=2, foo)

但是,从Python 3开始,filter无论如何都将返回一个迭代器,所以列表理解是最好的选择,正如Alex建议的那样。

答案 2 :(得分:7)

这不是一个正确的答案(因为我认为你已经有了一些相当不错的答案),但是......你考虑过使用<id>:<name>字典而不是字典列表吗?

答案 3 :(得分:3)

# assume ls contains your list
for i in range(len(ls)):
    if ls[i]['id'] == 2:
        del ls[i]
        break

平均可能比列表推导方法更快,因为如果它在早期找到有问题的项目,它就不会遍历整个列表。

答案 4 :(得分:1)

您可以尝试以下操作:

a = [{'id': 1, 'name': 'paul'},
     {'id': 2, 'name': 'john'}]

for e in range(len(a) - 1, -1, -1):
    if a[e]['id'] == 2:
        a.pop(e)

如果你不能从头开始弹出 - 从结尾弹出,它不会破坏for循环。

答案 5 :(得分:0)

您可以尝试以下几行:

def destructively_remove_if(predicate, list):
      for k in xrange(len(list)):
          if predicate(list[k]):
              del list[k]
              break
      return list

  list = [
      { 'id': 1, 'name': 'John' },
      { 'id': 2, 'name': 'Karl' },
      { 'id': 3, 'name': 'Desdemona' } 
  ]

  print "Before:", list
  destructively_remove_if(lambda p: p["id"] == 2, list)
  print "After:", list

除非你构建类似于数据索引的东西,否则我 不要以为你做的比做蛮力的表更好 扫描“整个列表。如果您的数据按键排序 正在使用,您可能可以使用 bisect 模块 找到你要找的对象有点快。

答案 6 :(得分:0)

假设您的python版本为3.6或更高版本,并且您不需要删除的项目,则价格会更低...

如果列表中的词典是唯一的:

for i in range(len(dicts)):
    if dicts[i].get('id') == 2:
        del dicts[i]
        break

如果要删除所有匹配的项目:

for i in range(len(dicts)):
    if dicts[i].get('id') == 2:
        del dicts[i]

您也可以对此进行确定,以确保无论python版本如何,获取id密钥都不会引发keyerror

  

如果dicts [i] .get('id',None)== 2