Python list comprehension multiple expressions

10 Things to Know to Master Comprehensions in Python

List comprehension, dictionary comprehension, and more

Yong Cui

Sep 1, 2021·7 min read

Photo by Jay Patel on Unsplash

One important Pythonic feature that we use often is comprehensions. The most notable one is list comprehension a concise way to create lists. However, this feature can be confusing to beginners. Moreover, beyond list comprehension, there are several other related comprehension-based techniques, such as dictionary and set comprehensions.

Without further ado, lets get it started exploring 10 essential building blocks to understand comprehensions in Python.

1. Basic Form of List Comprehension

Lets start with the syntax. List comprehension is a concise way of creating lists from an existing iterable.

# The syntax for list comprehension
created_list = [expression for item in iterable]

What it does is to apply each of the item to the expression, by turn, which will return a value that will go to the created list as an item. To help you understand what it means, we can use the expanded form a for loop to illustrate how list comprehension works.

# The expanded form using for loop
created_list = []
for item in iterable:
created_item = certain_expression_to_process_the_item
created_list.append[created_item]

The created_list in the for loop will be equivalent to the one created from the list comprehension.

2. Creating a List From an Iterable

The most common use case is to create a list object from an existing iterable. It should be noted that Python has many kinds of iterables, such as strings, lists, sets, dictionaries, map objects, and so on. Any iterable can be used in a list comprehension. Its kind of unnecessary to show you all the examples. Thus, Ill simply show you list comprehensions from sets, dictionaries, and tuples.

List Comprehensions [set, dict, and tuple]

If you know the syntax of list comprehension, everything should be straightforward, except for the dict object, which I use the items method to retrieve the key-value pairs. In the expression, both key and value can be used to create the items for the list object.

3. Keeping the Needed Items

When we use list comprehension, we dont always want to send every item of the existing list to the expression for creating the new item. In this case, we can apply a conditional evaluation to check whether an item should be included. The general form is below:

# condition in a list comprehension
items = [expression for item in iterable if condition]
# Equivalent for loop
items = []
for item in iterable:
if condition_of_the_item:
created_item = expression
items.append[created_item]

The following is a trivial example showing you we keep only fibonacci sequences that are multiples of three or five.

>>> fibonacci = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> squares_fib = [x*x for x in fibonacci if x%3 == 0 or x%5 == 0]
>>> squares_fib
[0, 9, 25, 441]

4. Conditional Assignment [Ternary Expression]

One special expression in Python is ternary expression. Instead of writing the multi-line version of ifelse statement, you can use a one-liner.

a if condition else b

This expression evaluates to a if the condition is True while to b if the condition is False. We can apply the ternary expression in the list comprehension, which will do conditional assignment on the items of the original list: [expr0 if condition else expr1 for item in iterable]. Applying this syntax, we have the following example:

List Comprehension: Ternary Expression

5. Nested List Comprehensions

So far, you should understand that from a certain perspective, list comprehension is a way to replace a for loop to create a list object. We also know that we can have nested for loops:

for items in items_list:
for item in items:
expression

Is it possible to convert this nested for loop to a list comprehension? Yes, we can use nested list comprehension. Observe the following example.

Nested List Comprehension

Find it hard to understand? You can simply read from the first for loop, which is the first level, and the second for loop is the inner loop. With that, everything will be easy to understand.

Technically, you can write multiple levels for the nested list comprehensions. However, for better readability, I dont think its a good idea to have more than two levels.

6. Replacing map[]

The built-in map function applies a function to each item of an iterable, creating another iterable the map object. As you can see, its very similar to list comprehension, in which the expression is applied to each of the iterables items. Thus, some people use map to create a list, as shown below.

>>> animals = ['tiger', 'Lion', 'doG', 'CAT']
>>> uppercased_animals = list[map[str.upper, animals]]
>>> uppercased_animals
['TIGER', 'LION', 'DOG', 'CAT']

It should be noted that we need to use a list constructor, in which we pass the map object, an iterable that is different from a list object. Instead of using the map function, we can use list comprehension.

>>> [x.upper[] for x in animals]
['TIGER', 'LION', 'DOG', 'CAT']

Although the map function has other usages, when the goal is to create a list object, list comprehension is usually more readable.

7. Using List Constructor When Applicable

One misuse about list comprehension to avoid is to use list constructor when applicable. Whats list constructor? The so-called list function.

class list[[iterable]]Rather than being a function, list is actually a mutable sequence type, as documented in Lists and Sequence Types list, tuple, range.

As indicated by the function signature, list constructor can take any iterable directly. Thus, when you dont manipulate the item in an iterable, you should send it directly to the list constructor. Consider the following code snippet for such contrast.

List Comprehension vs. List Constructor

The above example just provides a proof of concept. The take-home message is that unless you manipulate the items of the iterable [e.g., applying a function], you should use the list constructor directly. The following is another example, for interested readers.

>>> numbers_dict = {1: 'one', 2: 'two'}
>>> # list comprehension
>>> [[key, value] for key, value in numbers_dict.items[]]
[[1, 'one'], [2, 'two']]
>>> # list constructor
>>> list[numbers_dict.items[]]
[[1, 'one'], [2, 'two']]

8. Set Comprehension

Besides list comprehension, we can also use comprehension to create a set from an existing iterable: {expression for item in iterable}. Compared to list comprehension, set comprehension uses curly braces instead of square brackets. There are two more things to note when we use set comprehension.

  • There will be no duplicates in the created set. Even if duplicated items will be created from the expression, only one copy will be kept in the final set object.
  • By design, set objects can only store hashable data, such as strings and integers, but not lists and dictionaries.

The following code shows you a use case of set comprehension. As a side note, many other operations discussed above, such as conditional assignment, are also supported by set comprehension. Interested readers can explore these features.

>>> numbers = [-3, -2, -1, 1, 2, 3, 4, 5]
>>> squares_set = {x*x for x in numbers}
>>> squares_set
{1, 4, 9, 16, 25}

9. Dict Comprehension

As you may have guessed, besides list and set comprehension, you wont be surprised to know that we can use dict comprehension to create a dict object: {key_expr: value_expr for item in iterable}. Unlike the other two comprehensions, dictionary comprehension takes two expressions, one for the key and the other for the value. One thing to note is that dictionary keys have to be hashable, so the key_expr should produce a hashable object.

>>> numbers = [-3, -2, -1, 1, 2, 3, 4, 5]
>>> squares_dict = {x: x*x for x in numbers}
>>> squares_dict
{-3: 9, -2: 4, -1: 1, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

10. Generator Expression

Typically, the most commonly used built-in data containers are tuple, list, dictionary, and set. We have comprehension for the latter three. Is there a tuple comprehension? Unfortunately, there is no such thing. In terms of the potential syntax, you may think of a possible implementation: [expression for item in iterable].

Notice the use of parentheses? Just like tuples. Interestingly, this usage does exist, but its not creating a tuple object. Instead, its creating a generator, which is a special kind of iterator with better memory efficiency. Because unlike other iterators which load all items in the memory, generators render an item when needed, which avoids the overhead of loading all the items. Thus, when you deal with a large number of data, consider using generators instead of other iterators, such as lists or tuples. The following code shows you a use case when generators are preferred.

>>> numbers = list[range[10_000_000]]
>>> # Creating a list of squares using list comprehension
>>> squares = [x*x for x in numbers]
>>> squares.__sizeof__[]
89095144
>>> # Using generator instead
>>> squares_gen = [x*x for x in numbers]
>>> squares_gen.__sizeof__[]
96
>>> # Having the same result
>>> sum[squares] == sum[squares_gen]
True

As you can see, the intermediate step [list vs. generator] shows you that a generator, which is created from the generator expression, has a trivial size compared to the list object, highlighting the performance of generators.

Conclusion

This article reviews all essential things that you need to know the comprehension techniques in Python. These techniques are concise ways to create the needed data structures, and you should use them whenever applicable.

Thanks for reading this article. Stay connected by signing up my newsletter. Not a Medium member yet? Support my writing by using my membership link.

Video liên quan

Chủ Đề