字典推导式中的多个键值对

38

我想要使用字典推导式创建多个键值对,代码如下:

{'ID': (e[0]), 'post_author': (e[1]) for e in wp_users}

我收到了"missing ','"的错误提示。

我也试过这样:

[{'ID': (e[0]), 'post_author': (e[1])} for e in wp_users]

我收到了 "list indices must be integers, not str" 的错误提示。

虽然我理解这个错误,但不确定最佳的纠正方式,也不知道使用字典推导是否可以有多个键值对?


8
第二种方法对我来说很好用。wp_users 是哪种类型? - RodrigoOlmo
1
这里的 e[0] 周围的括号是无用的。 - Eric
当我尝试访问“post_author”时,例如:wp_users_list ['post_author']是当我得到“列表索引必须是整数,而不是str”时。 - mdxprograms
因为您正在创建一个列表而不是字典,所以在第二个选项中尝试循环遍历它,或者使用 wp_users_list[0]['post_author'] - salparadise
4个回答

41

一个字典推导式在每次迭代中只能生成一个键值对。因此,关键是生成额外的循环来分离这些键值对:

{k: v for e in wp_users for k, v in zip(('ID', 'post_author'), e)}

这相当于:

result = {}
for e in wp_users:
    for k, v in zip(('ID', 'post_author'), e):
        result[k] = v

请注意,这只是在您的wp_users列表中重复使用了这两个键,因此您不断地用新值替换相同的键!在这种情况下,您可以直接采用最后一个条目:

result = dict(zip(('ID', 'post_author'), wp_users[-1]))

然而,您没有分享您所期望的输出。

如果想要一个包含两个键的字典列表,则需要将上述表达式应用于每个wp_users条目的列表推导。

result = [dict(zip(('ID', 'post_author'), e)) for e in wp_users]

这将产生与您自己的第二次尝试相同的输出,但现在您有一个字典的列表。 您将需要使用整数索引来获取其中一个字典对象或使用进一步的循环。


21

我无意中发现了这个旧问题,但我对被接受的答案并不满意。

被接受的答案

被接受的答案有什么令人不安的地方?请考虑以下内容:

>>> wp_users = [(1, 'Bill'), (2, 'Bob')]
>>> {k: v for e in wp_users for k, v in zip(('ID', 'post_author'), e)}
{'ID': 2, 'post_author': 'Bob'}
  • 在对wp_users进行第一次迭代时,e = (1, 'Bill'),字典为{'ID':1, 'post_author':'Bill'}
  • 在对wp_users进行第二次迭代时,e = (2, 'Bob'),字典完全被覆盖为{'ID':2, 'post_author':'Bob'}

每次迭代时,字典的所有值都会被覆盖。可以避免循环并直接跳到wp_users的最后一个元素:

>>> {k: v for e in wp_users for k, v in zip(('ID', 'post_author'), e)}
{'ID': 2, 'post_author': 'Bob'}

或者:

>>> dict(zip(('ID', 'post_author'), wp_users[-1]))
{'ID': 2, 'post_author': 'Bob'}

我认为那不是您想要的。

您试图实现的目标还不太清楚,但我看到两个选项:您有一个用户列表 (id, post_author),您想要创建一个字典列表(每个用户一个字典)或元组字典(每个字段一个元组)。您可以将第一个版本视为按行呈现,将第二个版本视为同一数据的按列呈现。

字典列表

请尝试以下操作:

>>> [dict(zip(('ID', 'post_author'), user)) for user in wp_users]
[{'ID': 1, 'post_author': 'Bill'}, {'ID': 2, 'post_author': 'Bob'}]

对于每个userzip将创建元组('ID', id)('post_author', author),而dict会生成字典。现在您可以像这样访问字段:

>>> ds = [dict(zip(('ID', 'post_author'), user)) for user in wp_users]
>>> ds[0]['post_author']
'Bill'

元组字典

这种情况比较少见,但您可能需要一个值为元组的字典:


>>> dict(zip(('ID', 'post_author'), zip(*wp_users)))
{'ID': (1, 2), 'post_author': ('Bill', 'Bob')}

zip(*wp_users) 简单地创建了一个元组列表 [(id1, id2, ...), (post_author1, post_author2, ...)],其余部分与之前的版本类似。

>>> d = dict(zip(('ID', 'post_author'), zip(*wp_users)))
>>> d['post_author'][0]
'Bill'

奖金

要从行视图中提取一列:

>>> [d['ID'] for d in ds]
[1, 2]
从列视图中提取一行:
>>> {k:vs[1] for k, vs in d.items()}
{'ID': 2, 'post_author': 'Bob'}

4
看,这就是没有适当的 [mcve] 的问题所带来的麻烦,你不会总是看到输入与其所要求的并不符合。很明显,我们无法清楚地知道 OP 期望得到什么输出。 - Martijn Pieters

1
我认为你的问题在于第二个版本创建了一个字典列表,而不仅仅是一个字典。你试图使用一个字符串访问列表,这会引发错误:
>>> obj = [{'data1': 67, 'data2': 78}]
>>> obj[0]['data1']
67
>>> obj['data1']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not str
>>> 

相反,只需访问第二个版本作为 `wp_list[0]['post_author'],它应该可以正常工作:

>>> wp_users = ('Bob', 'Joe', 'Sally')
>>> wp_list = [{'ID': (e[0]), 'post_author': (e[1])} for e in wp_users]
>>> wp_list[0]['post_author']
'o'
>>> wp_list[1]['post_author']
'o'
>>> wp_list[2]['post_author']
'a'
>>> 

-1

一个适用于 Python >= 3.9 的解决方案(按照您的需求更改键和值,尽管我建议保持元组的解包状态,即使您要构建一个新的元组)

from itertools import tee
a,b = tee(tuple_collection)
c = {k:v for k,v in a} | {v:k for k,v in b}

我其实不确定这样做是否比将两个键放在同一个字典中更有效率。这取决于tee的好坏以及|的优化程度吧。不过看起来确实很简洁。
如果你使用的是Python 3.5+,你可以这样做。
c = dict(**{k:v for k,v in a}, **{v:k for k,v in b})

或者

c = {k:v for k,v in a}
c.update({v:k for k,v in b})

相反。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接