# Dict Comprehension ?

Ben Finney bignose+hates-spam at benfinney.id.au
Tue Oct 7 02:27:41 CEST 2008

```"Ernst-Ludwig Brust" <seinnixgud at online.de> writes:

> Given 2 Number-Lists say l0 and l1,
> count the various positiv differences between the 2 lists
>
> the following part works:
>
> dif=[abs(x-y) for x in l0 for y in l1]
> da={}
> for d in dif: da[d]=da.get(d,0)+1
>
> i wonder, if there is a way, to avoid the list dif

You can iterate over any iterable. A generator is iterable; and you
can (among other ways) create a generator with a generator
expression.

Fortunately, you already know how to write any generator expression:
it's the same as a list comprehension, but without the square
brackets.

So, instead of making a list of differences and iterating over it::

>>> seq_a = [15, 17, 26]
>>> seq_b = [14, 17, 22]
>>> diffs = [abs(a - b) for a in seq_a for b in seq_b]
>>> diff_accumulator = {}
>>> for diff in diffs:
...     diff_accumulator[diff] = diff_accumulator.get(diff, 0) + 1
...
>>> diff_accumulator
{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 7: 1, 9: 1, 12: 1}

you can skip the intermediate list creation and iterate over the
generator made by an identical generator expression::

>>> seq_a = [15, 17, 26]
>>> seq_b = [14, 17, 22]
>>> diff_accumulator = {}
>>> for diff in (abs(a - b) for a in seq_a for b in seq_b):
...     diff_accumulator[diff] = diff_accumulator.get(diff, 0) + 1
...
>>> diff_accumulator
{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 7: 1, 9: 1, 12: 1}

If you wanted to avoid starting the dict empty and writing the ‘for’
loop yourself, you could even create your dict from a generator (the
dict type can make a new dict from any key+value iterable) with the
help of the standard library ‘itertools’ module::

>>> import itertools
>>> seq_a = [15, 17, 26]
>>> seq_b = [14, 17, 22]
>>> diffs = [abs(a - b) for a in seq_a for b in seq_b]
>>> diff_accumulator = dict(
...     (diff, len(list(items)))
...     for (diff, items) in itertools.groupby(diffs)
...     )
>>> diff_accumulator
{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 7: 1, 9: 1, 12: 1}

This also, of course, benefits from the earlier approach to iterate
over the diffs directly instead of an intermediate list::

>>> import itertools
>>> seq_a = [15, 17, 26]
>>> seq_b = [14, 17, 22]
>>> diff_accumulator = dict(
...     (diff, len(list(items)))
...     for (diff, items) in itertools.groupby(
...         abs(a - b) for a in seq_a for b in seq_b
...         )
...     )
>>> diff_accumulator
{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 7: 1, 9: 1, 12: 1}

So, generator expressions can be a powerful way to clarify the purpose
of a section of code. They can be over-used, though: don't use them
unless they actually *do* clarify the code; sometimes an explicit
looping construct is clearer.

--
\        “Like the creators of sitcoms or junk food or package tours, |
`\         Java's designers were consciously designing a product for |
_o__)                       people not as smart as them.” —Paul Graham |
Ben Finney

```