Python:从列表中删除字典

joh*_*ald 51 python dictionary list

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

[{'id': 1, 'name': 'paul'},
 {'id': 2, 'name': 'john'}]
Run Code Online (Sandbox Code Playgroud)

我想删除id2(或名称john)的字典,这是以编程方式进行此操作的最有效方法(也就是说,我不知道列表中条目的索引,所以它可以不要弹出).

Ale*_*lli 94

thelist[:] = [d for d in thelist if d.get('id') != 2]
Run Code Online (Sandbox Code Playgroud)

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

在旧的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
Run Code Online (Sandbox Code Playgroud)

其中大约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
Run Code Online (Sandbox Code Playgroud)

(当然,如果您使用的是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
Run Code Online (Sandbox Code Playgroud)

正如我们所知,可以删除洗牌,因为已经有三个等间隔的序列要删除.而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
Run Code Online (Sandbox Code Playgroud)

完全颈部和颈部,甚至只需要移除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
Run Code Online (Sandbox Code Playgroud)

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

  • @imagist,它仍然是最快的 - 测量它,为了善良,不要只是假设你知道你在说什么,尤其是.当你显然没有;-),*ESPECIALLY*当要删除的项目是第一个(它避免移动其他所有项目).并且在原始问题中没有迹象表明列表中的每个字典必须始终具有与"id"对应的不同值. (14认同)
  • @kzh:`theList [:]`相当于`theList [0:len(theList)]`.在这种情况下,它意味着"就地更改列表". (4认同)
  • `theList [:] = ..`和`theList = ..`有什么区别? (4认同)

Mer*_*son 9

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

[x for x in foo if not (2 == x.get('id'))]
Run Code Online (Sandbox Code Playgroud)

替代'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建议的那样.


for*_*ran 7

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

  • ...只要您根本不关心保留项目的顺序,从不希望通过其他属性删除事物,对从未允许与该属性重复任何内容等感到满意,等等–太多了超出OP所表达的任何规范的限制,以使该建议合理;-)。 (3认同)
  • +1:"如果这很难,你做错了." 如果要按属性删除内容,请使用由属性键入的字典.更简单. (2认同)

Ima*_*ist 7

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

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