Search code examples
pythonpython-3.xdictionarydict-comprehension

if-else in a dictionary comprehension


Is it possible to use the else statement (and if yes, how?) in a dictcomp?

It is not possible to use else as part of the comprehension itself (see this) but at least in list and set comprehensions it is possible to use the conditional_expression (see this).

An example for listcomp is here.

My example code is:

converters = {"id": int}
rows = [{"id": "1", "name": "foo"}, {"id": "2", "name": "bar"}]
for row in rows:
    row = {k: converters[k](v) if k in converters else k:v for k,v in row.items()}
    print(row)

This does not work.

The weird part is that

row = {k: converters[k](v) if k in converters for k, v in row.items()} does not work either, although it should be ok.

row = {k: converters[k](v) for k, v in row.items() if k in converters} does work,but this is not the result I want.
row = {k: converters[k](v) for k, v in row.items() if k in converters else k:v} should not work, as I pointed out above.

I know that I could bypass the problem by using two dictcomps, but I want to know why this does not work.


Solution

  • That's because the conditional applies for the value of the dictionary, not for the key value pair, i.e it is evaluated as:

    row = {k: (converters[k](v) if k in converters else k:v) for k,v in row.items()}
    

    and k:v is not syntactically valid here, it's only valid inside a pair of curly brackets or in a function signature (so, you could place k:v in brackets and fix the SyntaxError but, that changes the end result).

    The solution is to simply supply the value in the conditional since that is what changes:

    row = {k: converters[k](v) if k in converters else v for k,v in row.items()}
    

    Another option, of course, is to instead supply tuples to the dict constructor:

    row = dict((k, converters[k](v)) if k in converters else (k,v) for k,v in row.items())