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



More information about the Python-list mailing list