假设我有一个字典列表,比如:
[{'id': 1, 'name': 'paul'},
{'id': 2, 'name': 'john'}]
我想从字典列表中删除id
为2(或名称'john'
)的字典,以编程方式最高效的方法是什么(也就是说,我不知道列表中条目的索引,所以不能简单地使用pop函数)。
假设我有一个字典列表,比如:
[{'id': 1, 'name': 'paul'},
{'id': 2, 'name': 'john'}]
我想从字典列表中删除id
为2(或名称'john'
)的字典,以编程方式最高效的方法是什么(也就是说,我不知道列表中条目的索引,所以不能简单地使用pop函数)。
thelist[:] = [d for d in thelist if d.get('id') != 2]
编辑: 由于在评论中对这段代码的性能提出了一些疑问(有些是基于误解Python的性能特征,有些是假设列表中仅有一个字典具有'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
方法)-但是如果满足删除条件的字典数量不止一个,则此代码会出现问题。 将其泛化,我们有:
$ 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
因为我们已经知道有三个等间距的字典要去除,所以可以删除洗牌操作。列表推导式不变,表现良好:
$ 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所说的“过早优化是编程之恶”的特别恶劣的示例!-)
[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建议的那样使用列表推导式确实是最佳选择。
# assume ls contains your list
for i in range(len(ls)):
if ls[i]['id'] == 2:
del ls[i]
break
这种方法通常会比列表推导式的方法快,因为如果它在早期找到所需项,它就不会遍历整个列表。
id
的键,例如:if ls[i]['name'] == 'john':
将匹配并删除该字典。 - twknab我认为你已经有了一些很好的答案,这不是一个合适的答案,但是...你考虑过使用<id>:<name>
字典而不是字典列表吗?
假设您的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]
您可以这样做,以确保获取id键不会引发KeyError,而不管Python版本如何
如果dicts[i].get('id', None) == 2
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)
等待评论混淆... - hughdbrowna = list(range(5)) a [0, 1, 2, 3, 4] range(len(a) - 1, -1, -1) [4, 3, 2, 1, 0] range(len(a) - 1, 0, -1) [4, 3, 2, 1]
试试这个: 从列表中删除“joh”示例
for id,elements in enumerate(dictionary):
if elements['name']=='john':
del dictionary[id]
从有关通用拆包(Python 3.5及以上版本)的pep448的更新开始,当迭代一个字典列表时使用临时变量,比如说row,你可以将当前迭代的字典作为 **row 输入,合并新的键或使用布尔运算从字典列表中过滤掉一个或多个字典。
请记住 **row 将输出一个新的字典。
例如,您的起始字典列表:
data = [{'id': 1, 'name': 'paul'},{'id': 2, 'name': 'john'}]
如果我们想要过滤掉ID为2的数据:
data = [{**row} for row in data if row['id']!=2]
data = [{**row} for row in data if row['name']!='John']
虽然与问题不直接相关,但如果您想添加新的键:
data = [{**row, 'id_name':str(row['id'])+'_'+row['name']} for row in data]
if row['id']!=2
。 - Sharukh Rahmandef 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模块来更快地找到你要查找的对象。
xrange
在Python 2中使用,而在Python 3中现在被称为range
。正如所写的示例仍然是Python 2代码(请查看日期,观察将print
用作语句而不是函数)。 - Dirk
filter
将返回一个迭代器,这应该成为标准做法。 - Meredith L. PattersontheList[:]
相当于theList[0:len(theList)]
。在此上下文中,它意味着“就地更改 theList”。 - John FouhytheList[:] = ..
和theList = ..
有什么区别? - u0b34a0f6ae