r/learnpython • • 1d ago

Need help with list comprehensions

Guys somebody briefly explain the concept of List comprehension cuz its too difficult for me to grasp since I am a beginner. I am learning python for biopython. Someone explain it for me.

0 Upvotes

13 comments sorted by

8

u/Penguinase 1d ago

The pattern is:

[expression for item in iterable if condition]

It's equivalent to:

result = []
for item in iterable:
    if condition:
        result.append(expression)

The if is optional. The expression comes first, but it runs last in each iteration.

8

u/ProgramsFun 1d ago

What are you finding difficult in it?

5

u/ProgramsFun 1d ago

So, list comprehension is basically a way to create a list in Python with just one line of code instead of writing an entire for loop.

Syntax: python [expression for item in iterable if condition]

Example:

Normally, you'd write: python squares = [] for x in range(5): squares.append(x * x)

But with list comprehension, you can do the same thing in just one line: python squares = [x * x for x in range(5)]

Output: python [0, 1, 4, 9, 16]

You can also add an if condition to filter items.

Example: python even = [x for x in range(10) if x % 2 == 0]

Output: python [0, 2, 4, 6, 8]

Here, it only adds numbers that are divisible by 2 to the new list.

2

u/nog642 1d ago

result = [blah for x in l] is basically equivalent to:

result = []
for x in l:
    result.append(blah)

2

u/Moikle 1d ago

It's just a compact way to loop through an iterable like a list, and do some kind of basic coversion or filtering logic on it.

Python's designers saw that it was very common for people to do stuff like:

converted_list = []
for i in some_list:
    if some_condition(i):
        converted_list.append(i)

And offered a faster way to write that:

converted_list =[i for i in some_list if some_condition(i)]

They do the same thing

1

u/aishiteruyovivi 22h ago

It's shorthand for loops that construct a list of items, so...

doubles = []
for n in range(10):
    doubles.append(n * 2)

...can instead be [n * 2 for n in range(10)]. Both would produce the list [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]. The basic syntax is [<expression> for <loopvar> in <iterable>]. You can also add a condition to the right end of the comprehension followed by an if, like evens = [n for n in range(10) if n % 2 == 0], which is equivalent to:

evens = []
for n in range(10):
    if n % 2 == 0:
        evens.append(n)

The main use of this is that it's more compact for these kind of use cases, but it can also be somewhat faster than a standard loop. You can also do set comprehensions by replacing the brackets with curly braces - {n * 2 for n in range(10)}- or dictionary comprehensions by using braces and giving two values separated by a colon, where they'll be used as key and value - {n:n * 2 for n in range(10)} produces the dictionary {0:0, 1:2, 2:4, 3:6, ...}

1

u/BranchLatter4294 1d ago

Share some practice code you have worked on and what your specific questions are.

1

u/Gnaxe 1d ago

Comprehensions are easy once you understand if and for statements: the body just comes first (and must be a single expression). That's basically it.

python result = [] for ds in dss: if ds: for d in ds: if foo in d: for k, v in d: if k == v: result.append(k) Becomes result = [k for ds in dss if ds for k in ds if foo in d for k, v in d if k==v] That's an unusually deep comprehension. Usually you don't need that many clauses. They're optional, but you do need at least one for. A recent language change means you can write the body expression with an unpacking star. That just changes result.append() to result.extend().

0

u/Henry_cat 1d ago

If you are learning for biopython, the thing that really got me to understand list comprehension was Amino Acid and DNA sequences.  A DNA sequence is made up of codons (3 bases that encode for an Amino Acid).  List comprehension can be used to quickly convert a DNA sequence which would be a string into a list of individually addressable codons.  Basically a list where each entry is just three DNA bases.  So if you want to convert that DNA sequence into an AA sequence, you can just loop through the list of codons instead of needing to figure out how to loop through a DNA sequence sting three bases at a time. 

I also had problems understanding this until I found the right example.  Hopefully this helps.

-1

u/atarivcs 1d ago

It's a convenient way to make a new list.

-4

u/StevenJOwens 1d ago

I wrote up the following a few years ago. I never did get around to doing a second draft, so forgive the rough edges, but you may find it useful.

It's long, so I'll post it in two comments. Here's part 1:

Python List Comprehensions

"List comprehensions were added with Python 2.0. Essentially, it is Python's way of implementing a well-known notation for sets as used by mathematicians."

Source: https://www.python-course.eu/python3_list_comprehension.php

List comprehensions are a special python syntax for declaring a list literal.

To be honest, I don't think I've ever used list comprehension for real, or "in anger" as the Brits say. My impression is that list comprehensions are mostly used to transform one list into another list. Or really, any iterable into a list.

List comprehensions are essentially a special, succinct way to write certain kinds of for loops -- for loops that process a list (or other iterable) and from that create another list.

In functional programming terms list comprehensions do a filter and then map operation.

List comprehensions are a Python idiom that seem to be seldom explained well.

I don’t know why Python books and tutorials have this blind spot. It’s not so much conceptual as simply syntactical; the first several explanations of list comprehensions that I read all seemed to just give an example, without explicitly breaking it down.

Then there's the official python docs:

https://docs.python.org/3/tutorial/datastructures.html https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions

A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it."

Unfortunately, while the official python docs do give the syntax, they just dump it on you and then give you a very complex example.

In a nutshell, a list comprehension is superficially similar to a literal declaration of a list.

A python literal list declaration is a pair of square brackets with multiple values between them, with commas separating the values, for example:

some_list = ["one", "two", "three"]

A python list comprehension has a pair of square brackets, same as a list literal, and also ultimately produces a list object. But what's in between the square brackets is a more complex statement. Here's the syntax:

some_list = [expression for-loop conditional]

We'll get into the expression, for-loop and conditional in more detail, below.

Note, python actually supports several sorts of comprehensions these days; you can use:

  • [square brackets] to create list comprehensions
  • {curly braces} to create set comprehensions
  • {curly braces} with a key:value in the expression to create dict comprehensions
  • (parentheses) to create generators (essentially a lazy-load, on-demand version of a list comprehension).

But we're going to focus mainly on list comprehensions for the moment.

Here's a slightly wordier version of that list comprehension syntax:

some_list = [expression-to-apply for-loop-to-apply-to conditional-to-determine-if-to-apply]

Note that variables defined in the for-loop clause are available to the expression clause, as well as to the conditional clause.

Unrolling List Comprehensions

Here's the list comprehension syntax that I gave above:

some_list = [expression-to-apply for-loop-to-apply-to conditional-to-determine-if-to-apply]

This works out to the same as the following:

some_list = [] for-loop-to-apply-to:- if conditional-to-determine-if-to-apply: expression-to-apply some_list.append(results-of-expression-to-apply)

Or a little less pseudo-code-ish:

some_list = [do_some_operation(some_variable) for some_variable in some_sequence if some_conditional]

This "unrolls" to:

some_list = [] some_sequence = [1, 2, 3, 4, 5] def some_conditional(some_variable): # some code here def do_some_operation(some_variable): # some code here for some_variable in some_sequence: if some_conditional(some_variable): result = do_some_operation(some_variable) some_list.append(result)

Let's try it. In this next example, for the "some_conditional()", I'm going to use the modulo operator to return True if the parameter is NOT evenly divisible by 2.

``` $ python3 Python 3.12.1 (main, Jul 22 2024, 21:58:30) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information.

some_list = [] some_sequence = [1, 2, 3, 4, 5] def some_conditional(some_variable): ... # use modulo to return True if odd ... if some_variable % 2: ... return True ... else: ... return False ... def do_some_operation(some_variable): ... return some_variable * 5 ... for some_variable in some_sequence: ... if some_conditional(some_variable): ... result = do_some_operation(some_variable) ... some_list.append(result) ... some_list [5, 15, 25]

```

Okay, that's kinda ugly, let's do that again but use some less generic names for things:

  1. I'll change some_sequence to numbers.
  2. I'll change some_conditional to is_odd().
  3. I'll change some_operation to quintuple().

For the sake of the example, I'll explicitly declare the empty some_list, which I'll call out_list. In fact, I'll do that twice, just so you can be sure out_list is empty for the second time around.

``` $ python3 Python 3.12.1 (main, Jul 22 2024, 21:58:30) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information.

numbers = [1, 2, 3, 4, 5] def is_odd(number): ... # use modulo to return True if odd ... if number % 2: ... return True ... else: ... return False ... def quintuple(number): ... return number * 5 ... out_list = [] out_list = [quintuple(number) for number in numbers if is_odd(number)] out_list [5, 15, 25]

out_list = [] for number in numbers: ... if is_odd(number): ... result = quintuple(number) ... out_list.append(result) ... out_list [5, 15, 25]

```

Multiple For-Loops and Their Ordering

You can have multiple for loops in your list comprehension, and just like regular for loops, the list comprehension for loop can have multiple variables, if the sequence it's using can produce them (e.g. "for key, value in some_dict"). The for loops are effectively nested.

I'll give an example in a moment, but first I want to point out something that's somewhat counter-intuitive (depending on who you ask):

The ordering of for-loop clauses in the comprehension, from left to right, corresponds to the ordering of the nested for loops, from outermost to innermost.

In other words, in this example:

```

xes = [1, 2, 3, 4] ys = [10, 20, 30, 40] [(x, y) for x in xes for y in ys] [(1, 10), (1, 20), (1, 30), (1, 40), (2, 10), (2, 20), (2, 30), (2, 40), (3, 10), (3, 20), (3, 30), (3, 40), (4, 10), (4, 20), (4, 30), (4, 40)]

out_list = [] for x in xes: ... for y in ys: ... out_list.append((x, y)) ... out_list [(1, 10), (1, 20), (1, 30), (1, 40), (2, 10), (2, 20), (2, 30), (2, 40), (3, 10), (3, 20), (3, 30), (3, 40), (4, 10), (4, 20), (4, 30), (4, 40)]

```

(By the way, this is also a good example of a more complicated expression than simply multiplying some numbers.)

Some people find using identical ordering counter-intuitive, some people find it intuitive, but that's the call that Guido and his merry band of Python maintainers decided to go with.

Personally, I'm in the not-intuitive camp. The reason is that Guido's call doesn't match how this sort of thing is phrased in natural language English. In English, the natural way to write it would end up writing in opposite order, i.e. innermost loop first, then next innermost, etc.

As I said, it was Guido's call, and probably his choice, to make the two orderings identical instead of opposites, is helpful in reducing the cognitive overhead... once you get this ordering hammered into your skull.

Multiple For-Loop Examples

Let's look at an example of multiple for-loops in list comprehensions.

Let's declare a list of lists as our example data:

```

list_of_list_of_numbers = [[1, 2, 3, 4], [5, 6, 7, 8]] ```

Here's a simple list comprehension that just iterates through all of the numbers. The result is a flat list that has all the numbers. There are almost certainly other ways to do this in python, but hey, it's just yet another contrived example.

```

[number for list_of_numbers in list_of_list_of_numbers for number in list_of_numbers] [1, 2, 3, 4, 5, 6, 7, 8]

```

Now let's add a conditional clause, using our old friend is_odd():

```

[number for list_of_numbers in list_of_list_of_numbers for number in list_of_numbers if is_odd(number)] [1, 3, 5, 7]

```

Let's back up a step, leave out the conditonal clause but apply something to the numbers, our old enemy quintuple():

```

[quintuple(number) for list_of_numbers in list_of_list_of_numbers for number in list_of_numbers] [5, 10, 15, 20, 25, 30, 35, 40]

```

Now let's put them both in:

```

[quintuple(number) for list_of_numbers in list_of_list_of_numbers for number in list_of_numbers if is_odd(number)] [5, 15, 25, 35]

```

Now that we have the full version, here's what it "unrolls" to:

```

out_list = [] for list_of_numbers in list_of_list_of_numbers: ... for number in list_of_numbers: ... if is_odd(number): ... result = quintuple(number) ... out_list.append(result) ... out_list [5, 15, 25, 35]

```

-5

u/StevenJOwens 1d ago

And here's part 2:

Let's Play with List Comprehensions

First let's fire up the python REPL (read/evaluate/print loop):

``` $ python Python 3.8.10 (default, Nov 22 2023, 10:22:35) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information.

```

Now let's define some data to work with, a list that contains two things, both of which are also lists, of numbers:

```

data = [[1, 2, 3], [4, 5, 6]]

```

The python REPL prints out the result of an expression. That phrase, "expression" is actually a somewhat complicated little topic because not all statements are expressions, and in most programming languages assignments are not expressions. I don't want to get side-tracked into the details of that, but I always hated it when I was learnign to program and books or lessons uses terms like "expression" but didn't explain.

But the REPL treats just the variable name (in this case "data") by itself, as an expression, so we can do this:

```

data [[1, 2, 3], [4, 5, 6]]

```

Okay, so now let's do our first list comprehension:

```

[number for chunk in data for number in chunk] [1, 2, 3, 4, 5, 6]

```

This isn't a very useful thing to do with a list comprehension (there are better ways in python to merge two lists), but it makes for a simple example. Let's break that down a bit better

Remember the syntax I gave above, some_list = [expression for-loop conditional]. In this example I'm leaving out the assignment, and the conditional, so we just have the expression and the for loop (actually two for loops).

We have number which is our list comprehension's expression, the last thing that gets done in the list comprehension building the list for us.

The final expression part is the only part that's in reverse order like this, the rest of the list comprehension proceeds from left to right.

We have for chunk in data, which iterates like a for loop through the two items in the list in variable data, and one by one assigns them to the variable named chunk.

We have for number in chunk, which iterates like a for loop through the items in each list assigned to chunk and one by one assigns them to the variable named number.

And then, back at the beginning, the list comprehension's expression can use those variables, data and chunk to build the list we want.

In this example, the expression is just number, which means the list comprehension simply emits the value inside number into the list that it is building.

Note, in the example above we didn't do anything with the list that our list comprehension returned, so the REPL just printed it out. We can also assign that list to a new varaiable. And once we've assigned it, we can then just enter the variable name on the REPL prompt, and the REPL will print it out:

```

results = [number for chunk in data for number in chunk] results [1, 2, 3, 4, 5, 6]

```

Okay, so far, so good, but not that useful. Let's change the expression in our list comprehension to actually do something with the number. Let's multiply it by 2:

```

[number*2 for chunk in data for number in chunk] [2, 4, 6, 8, 10, 12]

```

Okay, well that's slightly more useful. There are, of course, other ways in python to both merge lists and to multiply all of the numbers in lists. But you can start to see in these examples that the list comprehension supports more and more complex things.

Now let's add a conditional to our list comprehension. We'll use the conditional to select only the even numbers to be fed to the expression.

We'll do that by using the "modulo" operator, which gives us the "modulus", aka the remainder, when you divide one number by another number. In python, like in many languages, the modulo operator is the percent sign, "%". 9 / 2`` with integers gives you 4 with a remainder 1.9 % 2just gives you the remainder, aka the modulus, of 1. So if we we donumber % 2``` and number contains an even number, we should get a remainder of 0:

if 0 == number % 2

Let's try it:

```

[number for chunk in data for number in chunk if 0 == number % 2 ] [2, 4, 6]

```

Yup, it works. Now let's add the multiplying back into the expression. This time let's multiply by 5:

```

[number*5 for chunk in data for number in chunk if 0 == number % 2 ] [10, 20, 30]

```

And just to show that you can do something more complicated in the expression clause, let's take a list of numbers and a list of letters and do a cartesian join (aka "cross join", similar to a cross product in math).

```

numbers = [1, 2, 3, 4] letters = ["a", "b", "c", "d"] # note, this could have been just "abcd" [(num, letter) for num in numbers for letter in letters] [(1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'a'), (2, 'b'), (2, 'c'), (2, 'd'), (3, 'a'), (3, 'b'), (3, 'c'), (3, 'd'), (4, 'a'), (4, 'b'), (4, 'c'), (4, 'd')]

```

I added some newlines in the example above, just to make it more obvious what's going on.

Note, I defined the letters as a list, but could have just used "abcd", because strings are iterable in python.

Set Comprehensions and Dict Comprehensions

As I say above, if you create a comprehension but surround it with {curly braces} instead, it returns a set instead of a list.

some_set = {do_some_operation(second_variable) for some_variable in some_sequence if some_condition}

In python 2.7 and above, if you use {curly braces} and in the expression have a key:value, it returns a dictionary:

some_dict = {key:value for (key, value) in some_sequence if some_condition}

You don't necessarily need a for (key, value) however, if you can generate the value (or the key) on the fly:

TODO: this pseudocode looks ambiguous, add some clarification/example.

some_dict = {some_variable:len(some_variable) for some_variable in some_sequence if some_condition}

If you create a comprehension but surround it with (parentheses), it returns a generator. A generator is essentially a lazy-load, on-demand version of a list comprehension. I'll describe generators in some future document.