The Tahaan
The Tahaan

Reputation: 7664

Python Dictionary comprehension to copy some pairs to a new dictionary

While playing around with input validation, specifically checking whether the supplied data have all the required attributes specified, and discarding attributes that I don't want, I did something like this:

>>> input = {'v1': 'val1', 'a2':'val2', 'a3':'val3'} 
>>> print input
{'v1': 'val1', 'a3': 'val3', 'a2': 'val2'}
>>> goodkeys = ['a2', 'a3']
>>> print goodkeys
['a2', 'a3']
>>> output = {}
>>> for a in goodkeys:    
...   output[a] = input[a]
... 
>>> 
>>> print output
{'a3': 'val3', 'a2': 'val2'}

Which of course works. But it occurred to me that it may be possible to do this in a more pythonic way. I tried:

>>> output = {}
>>> print output
{}
>>> output = { a:v for a in goodkeys for v in input[a] }
>>> print output
{'a3': '3', 'a2': '2'}
>>> 

Which for half a second I thought worked, then I realized that the values are wrong. Is there a pretty Python method? And what happened, where did Python get those values?

Upvotes: 0

Views: 1112

Answers (3)

Martijn Pieters
Martijn Pieters

Reputation: 1124070

You could use dictionary view objects:

goodkeys = {'a2', 'a3'}
output = {a: input[a] for a in input.viewkeys() & goodkeys}

where goodkeys is a set; dict.viewkeys() produces a set-like object that reflects the contents of the dictionary, and & goodkeys produces the intersection between those keys and the goodkeys set. That way you only ever produce keys that are both in the input dictionary and the set of good keys.

You created a nested loop like this:

for a in goodkeys:
    for v in input[a]:
        # v is a single character in the string value

Demo:

>>> input = {'v1': 'val1', 'a2':'val2', 'a3':'val3'} 
>>> goodkeys = {'a2', 'a3'}
>>> {a: input[a] for a in input.viewkeys() & goodkeys}
{'a3': 'val3', 'a2': 'val2'}

Since input[a] is a string, your inner loop produced the individual characters of each value:

>>> for a in goodkeys:
...     for v in input[a]:
...         print a, v
... 
a3 v
a3 a
a3 l
a3 3
a2 v
a2 a
a2 l
a2 2

Keys must be unique, so for a2, one of v, a, l and 2 is picked with the others discarded. The dictionary comprehension sets 'a2': 'v' first, then 'a2': 'a', etc. and only 'a2': '2' remains in the output, in the end.

You didn't need to use that inner loop; you could have stuck with:

{a: input[v] for a in goodkeys}

except that if there are any keys in goodkeys that are not in input you'd get a KeyError. The dictionary view approach neatly sidesteps that issue.

Upvotes: 3

grovesNL
grovesNL

Reputation: 6075

You may be over thinking this. Just try changing

output = { a:v for a in goodkeys for v in input[a] }

to simply...

output = { a:input[a] for a in goodkeys }

Upvotes: 0

Yann Vernier
Yann Vernier

Reputation: 15887

output = { a:input[a] for a in goodkeys }

Your second for iterates through the characters of the string values.

Upvotes: 0

Related Questions