0 added
0 removed
Original
2026-01-01
Modified
2026-02-21
1
<p>In a developer's everyday life, you will often see code that works with iterations because iterators are built into the language and tightly integrated into the standard library.</p>
1
<p>In a developer's everyday life, you will often see code that works with iterations because iterators are built into the language and tightly integrated into the standard library.</p>
2
<p>We usually assemble iterators and operations on them into data conveyors. Only at the end of each pipeline is reduce() or something else that doesn't pass elements on. Most of these pipelines consist of two types of operations:</p>
2
<p>We usually assemble iterators and operations on them into data conveyors. Only at the end of each pipeline is reduce() or something else that doesn't pass elements on. Most of these pipelines consist of two types of operations:</p>
3
<ol><li><strong>Converting individual elements</strong>with the map() function. It converts the entire stream using another function that handles the individual items</li>
3
<ol><li><strong>Converting individual elements</strong>with the map() function. It converts the entire stream using another function that handles the individual items</li>
4
<li><strong>Changing the composition of the elements</strong>via filtration or multiplication. The filter() function can filter the data. And the map() paired with the chain() from the itertools module turns each element into several without changing the nesting level</li>
4
<li><strong>Changing the composition of the elements</strong>via filtration or multiplication. The filter() function can filter the data. And the map() paired with the chain() from the itertools module turns each element into several without changing the nesting level</li>
5
</ol><p>For example, imagine we want a list of numbers like this:</p>
5
</ol><p>For example, imagine we want a list of numbers like this:</p>
6
<p>There are two copies each of increasing even numbers. Let's write a suitable pipeline:</p>
6
<p>There are two copies each of increasing even numbers. Let's write a suitable pipeline:</p>
7
<p>As you can see, the task is done by connecting ready-made elements rather than by writing all the code manually in the form of a for loop. We can already see the issue with our constructor: if there are no ready-made functions on elements or predicates, we will declare them beforehand or use lambda. Both options are inconvenient.</p>
7
<p>As you can see, the task is done by connecting ready-made elements rather than by writing all the code manually in the form of a for loop. We can already see the issue with our constructor: if there are no ready-made functions on elements or predicates, we will declare them beforehand or use lambda. Both options are inconvenient.</p>
8
<p>When another person reads our code with individual functions, they have to keep jumping back and forth through the code. And lambda looks unwieldy. But don't despair, Python has a syntax that can simplify working with conveyors.</p>
8
<p>When another person reads our code with individual functions, they have to keep jumping back and forth through the code. And lambda looks unwieldy. But don't despair, Python has a syntax that can simplify working with conveyors.</p>
9
<h2>What are list generators</h2>
9
<h2>What are list generators</h2>
10
<p>Let's try to solve the same problem another way:</p>
10
<p>Let's try to solve the same problem another way:</p>
11
<p>It's also a one-liner. You can get used to this syntax, although it may not look convenient now. Let's try to format the whole expression:</p>
11
<p>It's also a one-liner. You can get used to this syntax, although it may not look convenient now. Let's try to format the whole expression:</p>
12
<p>The code now looks like two nested loops. We can write similar code on regular ones:</p>
12
<p>The code now looks like two nested loops. We can write similar code on regular ones:</p>
13
<p>The code looks very similar, but there are two differences:</p>
13
<p>The code looks very similar, but there are two differences:</p>
14
<ul><li>For the first version, we create a new list, and for the second one, we modify a previously created one</li>
14
<ul><li>For the first version, we create a new list, and for the second one, we modify a previously created one</li>
15
<li>The first version is an expression, and the second is a set of instructions. Consequently, we can use the first option as a part of any other expressions. We didn't have to declare any auxiliary functions, and we didn't need any lambdas</li>
15
<li>The first version is an expression, and the second is a set of instructions. Consequently, we can use the first option as a part of any other expressions. We didn't have to declare any auxiliary functions, and we didn't need any lambdas</li>
16
</ul><p>Expressions that look like [… for … in …] are called<strong>list generators</strong>. Consider the components of the new syntax. List generators are defined as follows:</p>
16
</ul><p>Expressions that look like [… for … in …] are called<strong>list generators</strong>. Consider the components of the new syntax. List generators are defined as follows:</p>
17
<p>Let's look at this pattern in more detail:</p>
17
<p>Let's look at this pattern in more detail:</p>
18
<ul><li>EXPRESSION can use VARIABLE and is computed into a future list item</li>
18
<ul><li>EXPRESSION can use VARIABLE and is computed into a future list item</li>
19
<li>VARIABLE is the name with which the SOURCE elements are associated alternately</li>
19
<li>VARIABLE is the name with which the SOURCE elements are associated alternately</li>
20
<li>SOURCE - any iterator or iterated object</li>
20
<li>SOURCE - any iterator or iterated object</li>
21
<li>CONDITION - an expression that uses VARIABLE, computed at each iteration</li>
21
<li>CONDITION - an expression that uses VARIABLE, computed at each iteration</li>
22
</ul><p>If the condition is false, we skip the computing of the current iteration, so we add no new item to the final list. If we omit the condition and the if keyword, it will be equivalent to the if True. There can be several variables. Here, unpacking tuples and lists, including nested ones, works too.</p>
22
</ul><p>If the condition is false, we skip the computing of the current iteration, so we add no new item to the final list. If we omit the condition and the if keyword, it will be equivalent to the if True. There can be several variables. Here, unpacking tuples and lists, including nested ones, works too.</p>
23
<p>Here are a few examples:</p>
23
<p>Here are a few examples:</p>
24
<h2>When to use list generators</h2>
24
<h2>When to use list generators</h2>
25
<p>We saw above that list generators don't override all the built-in functions for dealing with iterators. One goes well with the other.</p>
25
<p>We saw above that list generators don't override all the built-in functions for dealing with iterators. One goes well with the other.</p>
26
<p>On the other hand, it's better not to mix list generators with map() and filter() - they're just interchangeable entities. Also, don't mix list generators with any side effects. The point is that generators allow you to write concise and compact code. There's no need to force the programmer to think about what will change and where when creating the list.</p>
26
<p>On the other hand, it's better not to mix list generators with map() and filter() - they're just interchangeable entities. Also, don't mix list generators with any side effects. The point is that generators allow you to write concise and compact code. There's no need to force the programmer to think about what will change and where when creating the list.</p>
27
<p>It applies not only to code with the map() and filter() functions but to any declarative pipelines in general. It's worth separating code written in different paradigms into separate sections. For example, I/O is one of the main types of side effects. It may be at the beginning of the pipeline or the end of it, but not in the middle.</p>
27
<p>It applies not only to code with the map() and filter() functions but to any declarative pipelines in general. It's worth separating code written in different paradigms into separate sections. For example, I/O is one of the main types of side effects. It may be at the beginning of the pipeline or the end of it, but not in the middle.</p>
28
<h2>How the declarative nature of list generators manifests</h2>
28
<h2>How the declarative nature of list generators manifests</h2>
29
<p>Let's see how the list generator differs from the explicitly imperative double loop. When you use loops, you can build a list and make other side effects - for example, change the objects in lists.</p>
29
<p>Let's see how the list generator differs from the explicitly imperative double loop. When you use loops, you can build a list and make other side effects - for example, change the objects in lists.</p>
30
<p>The loops perform repetitive actions, so side effects are okay in them.</p>
30
<p>The loops perform repetitive actions, so side effects are okay in them.</p>
31
<p>The list generators, in turn, describe what each item is, not how to get it from the outside world or output it to the console. You can look at the different parts and see that:</p>
31
<p>The list generators, in turn, describe what each item is, not how to get it from the outside world or output it to the console. You can look at the different parts and see that:</p>
32
<ul><li>The list generator describes the result. It says: "The resulting list is a list of numbers between 1 and 20"</li>
32
<ul><li>The list generator describes the result. It says: "The resulting list is a list of numbers between 1 and 20"</li>
33
<li>The procedural solution shows how to get a result. It says: "For each number in the range up to 20, add a number to the list"</li>
33
<li>The procedural solution shows how to get a result. It says: "For each number in the range up to 20, add a number to the list"</li>
34
</ul><p>The for loops look the same in both cases because Python loops are more declarative than some other languages. Thus, the for loops are considered imperative in Python because of their body, not their header.</p>
34
</ul><p>The for loops look the same in both cases because Python loops are more declarative than some other languages. Thus, the for loops are considered imperative in Python because of their body, not their header.</p>