multiple key value pairs in dict comprehension

牧云@^-^@ 提交于 2019-12-03 11:00:32

A dictionary comprehension can only ever produce one key-value pair per iteration. The trick then is to produce an extra loop to separate out the pairs:

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

This is equivalent to:

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

I came across this old question by accident, and I'm not convinced by the accepted answer.

Accepted answer

What is disturbing with the accepted answer? Consider this:

>>> 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'}
  • First iteration over wp_users, e = (1, 'Bill'), the dict is {'ID':1, 'post_author': 'Bill'}
  • Second iteration over wp_users, e = (2, 'Bob'), the dict is totally overwritten to {'ID':2, 'post_author': 'Bob'}

On every iteration, all the values of the dictonary are overwritten. You can avoid a loop and jump directly to the last element of wp_users:

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

Or:

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

I think that's not what you want.

What you are trying to achieve remains unclear, but I see two options: you have a list of users (id, post_author) and you want to create either a list of dictionaries (one dict per user) or a dictionary of tuples (one tuple per field). You can see the first version as a presentation by lines, and the second as a presentation by columns of the same data.

A list of dictionaries

Try this:

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

For each user, zip will create tuples ('ID', id) and ('post_author', author) and dict will generate the dictionaries. Now you can access to the fields like that:

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

A dictionary of tuples

That's more unusual, but you might want one dictionary whose values are tuples:

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

zip(*wp_users) simply creates a list of tuples [(id1, id2, ...), (post_author1, post_author2, ...)] and the rest is similar to the previous version.

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

Bonus

To extract a column from the line view:

>>> [d['ID'] for d in ds]
[1, 2]

To extract a line from the column view:

>>> {k:vs[1] for k, vs in d.items()}
{'ID': 2, 'post_author': 'Bob'}

I think your problem is that the second version is creating a list of dictionaries, not just one dictionary. You are trying to access a list with a string, which raises the error:

>>> 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
>>> 

Instead, just access the second version as `wp_list[0]['post_author'], and it should work fine:

>>> 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'
>>> 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!