question regarding list comprehensions

Bruno Desthuilliers bruno.42.desthuilliers at websiteburo.invalid
Mon Oct 20 12:45:52 EDT 2008


Pat a écrit :
> I have written chunks of Python code that look this:
> 
>     new_array = []
>     for a in array:
>         if not len( a ):
>             continue
>         new_array.append( a )


# à la lisp
new_array = filter(None, array)

# à la haskell
new_array = [a for a in array if a]

NB : all builtin empty builtin sequence type have a false boolen value, 
so 'not len(a)' and 'not a' are usually practicaly equivalent.

<ot>
While we're at it : I assume you mean 'list', not 'array'. That's at 
least how the builtin type is named...
</ot>


> and...
> 
>     string = ""
>     for r in results:
>         if not r.startswith( '#' ):
>             string =+ r


# à la lisp
string = "".join(filter(lambda s: not s.startwith('#'), results)

# à la haskell
string = "".join(r for r in results if not s.startswith('#'))

> It seems that a list comprehension could clean up the code, but I seem 
> to have a mental block on list comprehensions.  I've read up a lot on 
> this subject in my books and on the Internet and for whatever reason, 
> I'm having problems with this idiom (if that's the correct expression).
> 
> I've made a number of attempts to solve this on my own but I keep 
> getting errors.
> 
> Could someone please tell me how I could convert the above code to 
> something more elegant but readily understandable?

Basically, a list expression is written as (pseudo BNF):

'['<expression> for item in <iterable_expression> [if 
<conditional_expression>] ']'


where
- <expression> is any Python expression
- <iterable_expression> is any Python expression that evals to an iterable
- the 'if <conditional_expression>' part is optional

The resulting list will be obtained by evaluating <expression> for each 
item of <iterable_expression> for which <conditional_expression> is 
verified.

Since the main goal of list expressions is to build, well, lists, one 
usually uses an <expression> that evals to something we possibly want to 
be part of the new list !-)


Now for something more practical:

First let's rewrite your first snippet to get rid of the continue statement:

new_list = []
for item in source_list:
     if len(item):
         new_list.append(item)


And let's rewrite the second one to replace string concatenation with 
the more idiomatic (and much more flexible) list / join idiom:

new_list = []
for item in results:
     if not item.startswith('#'):
         new_list.append(item)

string = "".join(new_list)


You may start to see a kind of pattern here:

0.  new_list = []
1.  for item in sequence:
2.     if condition(item):
3.         new_list.append(item)

The equivalent list comprehension is:

new_list = [item for item in sequence if condition(item)]


Since you didn't apply any transformation to 'item' in your for loops, 
the <expression> part is as simple as it can be : we want the item, period.

The <iterable_expression> is here again quite simple : it's your source 
sequence. Ditto for the the <conditional_expression>.

IOW, your for-loop pattern is really:

new_list = []
for item in <iterable_expression>:
     if <conditional_expression>:
         new_list.append(<expression>)


Once you've identified the different parts, translating this for-loop to 
a list comprehension is mostly a matter of reording.


> Finally, if someone could point me to a good tutorial or explain list 
> compressions I would be forever in your debt.

http://en.wikipedia.org/wiki/List_comprehension
http://en.wikipedia.org/wiki/Set-builder_notation


HTH



More information about the Python-list mailing list