在python列表推导式中对字典列表进行更新的方法

Rhilip 2017-08-21 PM 5223℃ 2条

嗯,很绕的标题,但是这是为了解决这样一个问题:存在一个python列表,这个列表是由一系列的同样类型的字典组成,想要对该列表中的每一个字典进行更新(按照一定规则),并返回一个新的列表。

如此假定:

In [1]: d = [  # 原始列表                     
   ...: {"id":1,"name":"test1"},  
   ...: {"id":2,"name":"test2"}
   ...: ]  

In [2]: new_d = [  # 想要实现的效果,其中new_id的值为id值加1,并保留原字典中所有(未被更新)的项
   ...: {"id":1,"name":"test1","new_id":2},  
   ...: {"id":2,"name":"test2","new_id":3}   
   ...: ] 

使用最简单的方法就是,新建一个空列表,使用for循环遍历已有的字典列表,并对每个字典进行更新后添加进新建的空列表中。如下代码示意

In [3]: d_1 = []                                                 
   ...: for i in d:                                              
   ...:     i.update({"new_id": i["id"] + 1})                    
   ...:     d_1.append(i)                                        
   ...: d_1                                                      
   ...:                                                          
Out[3]:                                                          
[{'id': 1, 'name': 'test1', 'new_id': 2},                        
 {'id': 2, 'name': 'test2', 'new_id': 3}]                        

那有没有其他的方法馁,这就是本篇文章探讨了。。(最近奇技淫巧想多了。
就想用列表推导式来解决(毕竟如In[3] 的结构很容易改写成列表推导式)

In [4]: [i.update({"new_id": i["id"] + 1}) for i in d]
Out[4]: [None, None]

然而却得到了这样的结果?为什么馁。
查看官方文档dict.update

update([other])
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.

update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2).

好吧,dict.update返回的是None。那换个思路(对,上stackoverflow找找。
然后就看到了这篇python - update dict in list comprehension - Stack Overflow

来,我们来尝试下

In [5]: [{i:j for i,j in t.items() + [("new_id",t["id"]+1)]} for t in d]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-920aa73acbd6> in <module>()
----> 1 [{i:j for i,j in t.items() + [("new_id",t["id"]+1)]} for t in d]

<ipython-input-5-920aa73acbd6> in <listcomp>(.0)
----> 1 [{i:j for i,j in t.items() + [("new_id",t["id"]+1)]} for t in d]

TypeError: unsupported operand type(s) for +: 'dict_items' and 'list'

什么鬼嘛(摔 ,怪不得是0赞0踩的答案。直接抛出了TypeError。
原来,在python3中,dict.items()不像py2中一样返回一个列表,而是返回了一个dict_items对象。(参见2.7#dict.items 以及 3.6#dict.items

items()
Python2.7 : Return a copy of the dictionary’s list of (key, value) pairs.
Python3.6 : Return a new view of the dictionary’s items ((key, value) pairs).


这都不行,那也不行,那lambda。。 也不行。。。就在我想要放弃的时候
我神tm的想出了一个方法。

你update不是不返回吗,但是实际上字典列表中的字典已经更新了,那用or来过滤掉就行。就有了以下的列表推导式。
在执行的过程中因为 None or i => i让推导式中生成值为i。

In [6]: [i.update({"new_id": i["id"] + 1}) or i for i in d]
Out[6]:
[{'id': 1, 'name': 'test1', 'new_id': 2},
 {'id': 2, 'name': 'test2', 'new_id': 3}]

非特殊说明,本博所有文章均为博主原创。

评论啦~



已有 2 条评论


  1. chenyuan
    chenyuan

    那个SO的答案也不算错吧,dict_items对象转为list不就好了嘛

    [{i:j for i,j in list(t.items()) + [("new_id",t["id"]+1)]} for t in d]

    回复 2018-02-27 12:13
    1. Rhilip
      Rhilip 博主

      嗯,也对。
      刚开始接触Python的时候什么都不懂,就只能想这种吃力不讨好的解决方法。
      现在想想这种东西其实还是用list(map(function, list))来做更好些233333
      比如按照我这文所设的话应该是 list(map(lambda d:d["new_id"] = d["id"] + 1), l)

      回复 2018-04-05 00:50