108

I saw some code like:

foo = [x for x in bar if x.occupants > 1]

What does this mean, and how does it work?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Greg Flynn
  • 1,694
  • 2
  • 14
  • 15
  • 6
    @Greg, I googled for `python "* for * in * if"` and this was the first hit http://www.secnetix.de/olli/Python/list_comprehensions.hawk – John La Rooy Jun 25 '11 at 02:22
  • NOTE For duplicate closers: for questions asking *how to use* a list comprehension, consider https://stackoverflow.com/questions/25082410/apply-function-to-each-element-of-a-list for mapping, and https://stackoverflow.com/questions/4587915 for filtering. – Karl Knechtel Jun 30 '22 at 22:18

5 Answers5

124

The current answers are good, but do not talk about how they are just syntactic sugar to some pattern that we are so used to.

Let's start with an example, say we have 10 numbers, and we want a subset of those that are greater than, say, 5.

>>> numbers = [12, 34, 1, 4, 4, 67, 37, 9, 0, 81]

For the above task, the below approaches below are totally identical to one another, and go from most verbose to concise, readable and pythonic:

Approach 1

result = []
for index in range(len(numbers)):
    if numbers[index] > 5:
        result.append(numbers[index])
print result  #Prints [12, 34, 67, 37, 9, 81]

Approach 2 (Slightly cleaner, for-in loops)

result = []
for number in numbers:
    if number > 5:
        result.append(number)
print result  #Prints [12, 34, 67, 37, 9, 81]

Approach 3 (Enter List Comprehension)

result = [number for number in numbers if number > 5]

or more generally:

[function(number) for number in numbers if condition(number)]

where:

  • function(x) takes an x and transforms it into something useful (like for instance: x*x)
  • if condition(x) returns any False-y value (False, None, empty string, empty list, etc ..) then the current iteration will be skipped (think continue). If the function return a non-False-y value then the current value makes it to the final resultant array (and goes through the transformation step above).

To understand the syntax in a slightly different manner, look at the Bonus section below.

For further information, follow the tutorial all other answers have linked: List Comprehension


Bonus

(Slightly un-pythonic, but putting it here for sake of completeness)

The example above can be written as:

result = filter(lambda x: x > 5, numbers)

The general expression above can be written as:

result = map(function, filter(condition, numbers)) #result is a list in Py2
UltraInstinct
  • 43,308
  • 12
  • 81
  • 104
  • I'd term the first thing generally an *expression*; if it's just `x*x`, it often might just be there, rather than `square(x)`. It makes it even more painful then when I see that expression canned into a lambda, e.g. `[x**2 for x in range(5)]` vs. `map(lambda x: x**2, range(5))`. – Nick T Aug 29 '17 at 20:42
  • `list comprehension` without storing it in a var: `[print(i) for i in numbers]` – Timo Jul 10 '21 at 06:25
  • 1
    @Timo: Although your suggestion works, it is frowned upon. I personally hate it. The expression to the left of `for` (`print(i)` in your case) is supposed to be one that doesn't introduce side-effects like I/O (look up pure functions). As the answer calls out -- it's intended to be used as a 'transformation' and nothing more. If what you want to do is print, then print the result of the list comprehension or use a plain old for-loop to print individual items. – UltraInstinct Jul 10 '21 at 11:08
26

It's a list comprehension

foo will be a filtered list of bar containing the objects with the attribute occupants > 1

bar can be a list, set, dict or any other iterable

Here is an example to clarify

>>> class Bar(object):
...   def __init__(self, occupants):
...     self.occupants = occupants
... 
>>> bar=[Bar(0), Bar(1), Bar(2), Bar(3)]
>>> foo = [x for x in bar if x.occupants > 1]
>>> foo
[<__main__.Bar object at 0xb748516c>, <__main__.Bar object at 0xb748518c>]

So foo has 2 Bar objects, but how do we check which ones they are? Lets add a __repr__ method to Bar so it is more informative

>>> Bar.__repr__=lambda self:"Bar(occupants={0})".format(self.occupants)
>>> foo
[Bar(occupants=2), Bar(occupants=3)]
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • 2
    This answer is strictly better than the accepted answer, but not marked as such because it came in 4 minutes later. – user1717828 Oct 27 '15 at 22:03
2

Since the programming part of question is fully answered by others it is nice to know its relation to mathematics (set theory). Actually it is the Python implementation of Set builder notation:

Defining a set by axiom of specification:

B = { x є A : S(x) }

English translation: B is a set where its members are chosen from A, so B is a subset of A (B ⊂ A), where characteristic(s) specified by function S holds: S(x) == True

Defining B using list comprehension:

B = [x for x in A if S(x)]

So to build B with list comprehension, member(s) of B (denoted by x) are chosen from set A where S(x) == True (inclusion condition).

Note: Function S which returns a boolean is called predicate.

Xaqron
  • 29,931
  • 42
  • 140
  • 205
1

This return a list which contains all the elements in bar which have occupants > 1.

Kien Truong
  • 11,179
  • 2
  • 30
  • 36
1

The way this should work as far as I can tell is it checks to see if the list "bar" is empty (0) or consists of a singleton (1) via x.occupants where x is a defined item within the list bar and may have the characteristic of occupants. So foo gets called, moves through the list and then returns all items that pass the check condition which is x.occupant.

In a language like Java, you'd build a class called "x" where 'x' objects are then assigned to an array or similar. X would have a Field called "occupants" and each index would be checked with the x.occupants method which would return the number that is assigned to occupant. If that method returned greater than 1 (We assume an int here as a partial occupant would be odd.) the foo method (being called on the array or similar in question.) would then return an array or similar as defined in the foo method for this container array or what have you. The elements of the returned array would be the 'x' objects in the first array thingie that fit the criteria of "Greater than 1".

Python has built-in methods via list comprehension to deal with this in a much more succinct and vastly simplified way. Rather than implementing two full classes and several methods, I write that one line of code.