Search code examples
pythondictionarydictionary-comprehension

How does the in keyword of for loop in dictionary comprehensions work?


I have the following code snippet:

d = {1:2, 3:4, 5:6}
d1 = {k*2:v/2 for (k,v) in d.items()}
d2 = {k*2:v/2 for [k,v] in d.items()}
d3 = {k*2:v/2 for [k,v] in d.items() if [k,v] in d.items()}
print(d1)
print(d2)
print(d3)

this produces the following output:

{2: 1.0, 6: 2.0, 10: 3.0}
{2: 1.0, 6: 2.0, 10: 3.0}
{}

Dictionary comprehension of d1 makes sense as it checks for a tuple of form (k,v) in d.items() which is "kind of" list of tuples.

Dictionary comprehension of d2 should return {} as the list [k,v] isn't present in d.items() which is verified in dictionary comprehension of d3.

What is the difference between the in inside the for loop and using it outside?


Solution

  • From the for statement docs, for target_list in expression_list assigns objects from expression_list to target_list on each iteration. Just as list assignment in a regular statement assigns values

    >>> [x,y] = 100, 200
    >>> x
    100
    >>> y
    200
    

    for [k,v] in d.items() assigns the tuple values iterated by d.items() to x and y. As a result, {k*2:v/2 for (k,v) in d.items()} and {k*2:v/2 for [k,v] in d.items()} are the same.

    In your final case,

    d3 = {k*2:v/2 for [k,v] in d.items() if [k,v] in d.items()}
    

    The reason that d3 is empty is that the list [k,v] is not in d.items(), which iterates tuples. But the tuple k,v is. Change the check and you get the full dictionary again

    >>> d4 = {k*2:v/2 for [k,v] in d.items() if (k,v) in d.items()}
    >>> d4
    {2: 1.0, 6: 2.0, 10: 3.0}
    

    A final word on assignment to a list, I was surprised that it is not a syntax error. In fact, when you do the assignment, you get a tuple not a list. I'm puzzled!

    >>> z = [x,y] = (100,200)
    >>> z
    (100, 200)