1

I've been studying list comprehensions and something stopped me for days.

A simple list comprehension has the form

[expression for item in iterable]

the equivalent for loop is

li=[]
for item in iterable
    li.append(item)

If I'm right what generally a list comprehension does is it iterates through the iterable, evaluates the expression for each iteration, then appends it to the list.

Whatever should happen inside the for loop is written at the beginning of the liscomp.

We can think that in a listcomp Python only allows one expression and for loop's suit is permitted to have only an if clause or nested for loops.

To quote a book I was reading it states that

Since list comprehensions produce lists, that is, iterables, and since the syntax for list comprehensions requires an iterable, it is possible to nest list comprehensions. This is the equivalent of having nested for … in loops.

This confused my understanding.

Does this says the reason for having a listcomp like [s+z for s in iterable_1 for z in iterable_2]

Can someone please explain what this says.

Will Ness
  • 62,652
  • 8
  • 86
  • 167
  • 1
    a) it does not automatically do a list append, it simply `yield`s the statement you are passing to the `for ` clause b) nesting syntax is somewhat different, but you can find good reads about it, for instance https://spapas.github.io/2016/04/27/python-nested-list-comprehensions/ – wiesion Apr 24 '20 at 06:30
  • @wiesion I read it gave me some understanding. – Neminda Prabhashwara Apr 24 '20 at 06:47

1 Answers1

1

Your first translation should be

li=[]
for item in iterable: 
    li.append( expression(item) )

Your example [s+z for s in iterable_1 for z in iterable_2] is translated as

li=[]
for s in iterable_1:
    for z in iterable_2:
        li.append(s+z)

Congrats, you have discovered ... monads! which are essentially what you've described, nested loops.

Nested loops just produce a plain stream of results. Nested lists, when flattened, also turn into a plain stream of elements. That's the similarity. A lazy append is pretty much like yield.

Each monad type is defined by how it implements its version of the flatMap function, which is a map followed by the flattening of the nested structure. The flattening of the nested structure at each nesting level allows for an arbitrary depth of nesting to be flattened:

M [M (a)]  ==>  M (a)

M [M [M (a)]]  ==>   # flatten the outer two layers first:
                     M [M (a)]  ==>  M (a)
               OR:
               ==>  # flatten the inner two layers first:
                     M [M (a)]  ==>  M (a)

See the difference? There isn't any! Any type that does the above, is a "monad". Like lists.

So it is with loops as well, which can be nested to an arbitrary depth -- two, three, whatever, it doesn't matter.

That is the under the hood reason that we can use nested for loops in list comprehensions -- because list comprehensions are just like monadic chains of operations (and can be translated as such).

Will Ness
  • 62,652
  • 8
  • 86
  • 167