字典推导中的多个键值对
我正在尝试用字典推导式创建多个键值对,像这样:
{'ID': (e[0]), 'post_author': (e[1]) for e in wp_users}
但是我收到了 "缺少 ','"
的错误提示。
我也试过这样做:
[{'ID': (e[0]), 'post_author': (e[1])} for e in wp_users]
结果我得到了 "列表索引必须是整数,而不是字符串"
的错误。
我明白这个错误的意思,但不太确定该怎么修正它,以及用字典推导式是否真的可以创建多个键值对?
4 个回答
这是一个适用于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})
来代替。
我觉得你的问题在于第二个版本创建的是一个字典的列表,而不是一个字典。你试图用字符串去访问一个列表,这就导致了错误:
>>> 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'
>>>
我偶然看到这个旧问题,但我对被接受的答案并不太信服。
被接受的答案
那么,被接受的答案有什么问题呢?想想这个:
>>> 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'}]
对于每个 user
,zip
会创建元组 ('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'}
字典推导式每次迭代只能生成一个键值对。解决这个问题的方法是增加一个循环来分开这些键值对:
{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]
这样就能得到和你第二次尝试一样的输出,但现在你得到了一个字典列表。你需要用整数索引来访问其中一个字典对象,或者使用更多的循环。