Python dictionary comprehension

969 Views Asked by At

I have an input dictionary, I want to compare keys with another dictionary and if the key in the lookup dict gets hit, I want it to run a function as the value in the lookup dict.

The function in the look up dict will return a key, value pair that needs to be "updated" into a new dict. So here is an example:

out = {getattr(self,self.LOOKUPDICT[k])({k:query[k]}) for k in query.keys() if k not in self.exclusions}

The result of

getattr(self,self.LOOKUPDICT[k])({k:query[k]}) 

Is a dictionary that looks like this:

{'new_key':'new_val'}

In the comprehension I am getting

TypeError: unhashable type: 'dict'

Because it is expecting a key value pair.

Now I can just run a regular loop and an if statement, but I've become kind of adverse to those. Any workaround here?

TLDR: Dictionary comprehensions, how to insert a dict like this:

{{'k':'v'} for k in blahblah}

and make k the key and v a value. Note that the dict is returned from a function that gets ran in the expression.

2

There are 2 best solutions below

9
On BEST ANSWER

A dictionary comprehension expects separate key and value expressions:

{key_expr: value_expr for iterable [more if and for parts]}

but if your function produces both, you'd be better of using a dict() function with loop:

out = dict(getattr(self, self.LOOKUPDICT[k])({k: query[k]})
           for k in query if k not in self.exclusions)

because that function takes a sequence of tuples each containing a key-value pair.

Note that the .keys() call is not needed here; iteration directly over query is enough to yield keys. You could use set intersections here instead; the dict.keys() object is a dictionary view that itself acts as a set:

out = dict(getattr(self, self.LOOKUPDICT[k])({k: query[k]})
           for k in query.keys() - self.exclusions)

where the query.keys() - self.exclusions expression produces a set of keys that are in query but not in the iterable (set, dict, list, tuple, etc.) self.exclusions.

If the self.LOOKUPDICT values map to attributes on self, it could be useful to directly reach into the instance dictionary with:

attrs = vars(self)
out = dict(attrs[self.LOOKUPDICT[k]]({k: query[k]})
           for k in query.keys() - self.exclusions)
0
On

how about this

out = {_k:_v for k in query.keys() for _k,_v in getattr(self,self.LOOKUPDICT[k])({k:query[k]}).items() if k not in self.exclusions}

although this one is probably more what you're after

out = {_k:_v for k in set(query.keys()).difference(self.exclusions) for _k,_v in getattr(self,self.LOOKUPDICT[k])({k:query[k]}).items()}

the second one is better because it won't do the getattr(self,self.LOOKUPDICT[k])({k:query[k]}) call for the exclusions