Sequential Programs in Python

Learn to write clean, readable pipelines using comprehensions, map, filter, and functional patterns. Transform nested loops into elegant sequential programs.

~30 min readInteractive

A sequential program is a piece of code that takes a value, transforms it in a step-by-step manner, and returns a final result. In Python, we call these transformations pipelines or chains.

The most natural way to write sequential programs is through nested for loops. But nested loops have problems: they're hard to read, they require mutable state, and they tangle data transformation with side effects. In this guide, we'll explore a better way using functional programming patterns that Python makes easy.

"Inside every large program is a small program struggling to get out.": Tony Hoare
What you'll learn
  • How to write sequential programs using list comprehensions
  • The difference between map, filter, and flatMap
  • How list comprehensions translate to function chains
  • When to use comprehensions vs chained methods
  • How to handle nested transformations elegantly
  • Real-world patterns for processing data pipelines

The Problem: Nested Loops

Let's say we have a list of books with their authors, and we want to count how many have "Python" in the title:

books.py
books = [
  {"title": "FP in Python", "authors": ["Chiusano", "Bjarnason"]},
  {"title": "The Hobbit", "authors": ["Tolkien"]},
  {"title": "Modern Python", "authors": ["Urma", "Fusco"]}
]

# Using a for loop
titles = []
for book in books:
    titles.append(book["title"])

count = 0
for title in titles:
    if "Python" in title:
        count += 1

print(count)  # Output: 2
Output
2

This works, but notice what we're doing: we're mutating a list, iterating twice, and mixing data transformation with logic. Let's make this more elegant.

Solution
We can write this as a pipeline—a series of transformations where the output of one step becomes the input of the next.

Building Pipelines with map() and filter()

Python has two fundamental functions for building pipelines: map() applies a function to every element, and filter() keeps only elements that satisfy a condition.

books.py
books = [
  {"title": "FP in Python", "authors": ["Chiusano", "Bjarnason"]},
  {"title": "The Hobbit", "authors": ["Tolkien"]},
  {"title": "Modern Python", "authors": ["Urma", "Fusco"]}
]

# As a pipeline
titles = map(lambda book: book["title"], books)
python_books = filter(lambda title: "Python" in title, titles)
count = len(list(python_books))

print(count)  # Output: 2
Output
2

Each step of the pipeline has an input and output. The output type of one step must match the input type of the next. This creates a chain of transformations.

Map and Filter Visualization

Click through to see how data flows through each transformation:

books = [
Book(title="FP in Python", authors=["Chiusano","Bjarnason"]),
Book(title="The Hobbit", authors=["Tolkien"]),
Book(title="Modern Python", authors=["Urma","Fusco","Mycroft"]),
]

List Comprehensions: The Pythonic Way

Python gives us a more readable syntax for pipelines called list comprehensions. They look similar to set notation from mathematics and express intent more clearly than nested function calls.

books.py
# List comprehension version
count = len([title for book in books
             for title in [book["title"]]
             if "Python" in title])

print(count)  # Output: 2

# Even simpler
python_titles = [book["title"] for book in books
                 if "Python" in book["title"]]
count = len(python_titles)

print(count)  # Output: 2
Output
2

List comprehensions are equivalent to the chained map() and filter() calls, but they read like English: "for each book in books, if the title contains 'Python', extract the title."

Function Chains vs List Comprehensions
map() + filter()
Functional style. Functions are first-class, composable, and lazy (with iterators). Best for complex transformations or when you need to reuse functions.
List Comprehensions
Pythonic style. More readable for most people, easier to debug, eager evaluation. Best for one-off transformations and filtering.

Handling Nested Data with flatMap()

Now let's solve a harder problem. We have books with multiple authors, and we want a flat list of all authors:

books.py
books = [
  {"title": "FP in Python", "authors": ["Chiusano", "Bjarnason"]},
  {"title": "The Hobbit", "authors": ["Tolkien"]}
]

# Wrong: map gives us list of lists
result = list(map(lambda book: book["authors"], books))
print(result)
# [["Chiusano", "Bjarnason"], ["Tolkien"]]

# Right: use list comprehension to flatten
all_authors = [author for book in books
               for author in book["authors"]]
print(all_authors)
# ["Chiusano", "Bjarnason", "Tolkien"]
Output
['Chiusano', 'Bjarnason', 'Tolkien']

When you have a function that returns a list, and you want to flatten the results, you need flatMap (or in Python, the inner loop in a comprehension). This is one of the most important patterns in functional programming.

Map vs FlatMap

See the difference between map (nested) and flatMap (flattened):

Result: List of Lists (nested)
[
["Chiusano","Bjarnason"],
["Tolkien"],
]

Real Example: Movie Recommendations

Let's build a more complex pipeline. We have books with authors, and a function that returns movie adaptations for each author. We want to generate recommendation strings.

recommendations.py
books = [
  {"title": "FP in Python", "authors": ["Chiusano", "Bjarnason"]},
  {"title": "The Hobbit", "authors": ["Tolkien"]}
]

def book_adaptations(author):
    if author == "Tolkien":
        return [
            {"title": "An Unexpected Journey"},
            {"title": "The Desolation of Smaug"}
        ]
    return []

# Step-by-step pipeline
recommendations = []
for book in books:
    for author in book["authors"]:
        for movie in book_adaptations(author):
            rec = f'You may like {movie["title"]}, because you liked {author}'s {book["title"]}'
            recommendations.append(rec)

for rec in recommendations:
    print(rec)
Output
You may like An Unexpected Journey, because you liked Tolkien's The Hobbit
You may like The Desolation of Smaug, because you liked Tolkien's The Hobbit
Problem
This nested loop approach has three problems:
1. Mutable state (appending to a list)
2. Multiple levels of nesting make it hard to read
3. The loop body mixes data transformation with side effects

We can solve this with a list comprehension:

recommendations.py
# List comprehension version
recommendations = [
    f'You may like {movie["title"]}, because you liked {author}'s {book["title"]}'
    for book in books
    for author in book["authors"]
    for movie in book_adaptations(author)
]

for rec in recommendations:
    print(rec)
Output
You may like An Unexpected Journey, because you liked Tolkien's The Hobbit
You may like The Desolation of Smaug, because you liked Tolkien's The Hobbit
Solution

The comprehension reads from left to right, top to bottom:

  1. For each book in books
  2. For each author in book["authors"]
  3. For each movie in book_adaptations(author)
  4. Create a recommendation string using all three variables

This is declarative (what we want, not how to get it) and immutable (no intermediate state).


Filtering Inside Comprehensions

We can add conditions to filter at any level:

recommendations.py
# Only include movies from authors whose name starts with 'T'
recommendations = [
    f'You may like {movie["title"]}, because you liked {author}'s {book["title"]}'
    for book in books
    for author in book["authors"]
    if author.startswith('T')  # Add a condition here
    for movie in book_adaptations(author)
    if "Unexpected" in movie["title"]  # And here
]

for rec in recommendations:
    print(rec)
Output
You may like An Unexpected Journey, because you liked Tolkien's The Hobbit

The if clause filters at that level of the comprehension. This is cleaner than wrapping each loop with a conditional.


When to Use Comprehensions vs Methods

Choosing Your Style
Use comprehensions when...
You need to combine map + filter, have multiple nested loops, or need to combine values from different sources. They're more readable for complex transformations.
Use method chains when...
You have a simple pipeline (1-2 transformations), want to reuse the same functions, or need lazy evaluation. They're more composable and testable.
Use traditional loops when...
You need early exit (break), complex state management, or side effects beyond simple collection building. Readability should always come first.

Key Takeaways

  • Sequential programs transform data through a pipeline of steps, where each step's output feeds into the next step's input.
  • List comprehensions are Python's native way to express these pipelines. They combine map, filter, and flattening into one readable expression.
  • Immutability matters. Avoid mutating intermediate lists. Instead, build the final result in one expression.
  • Multiple loops become one comprehension. The order of for clauses matters—inner loops become subsequent for clauses.
  • Conditions filter at each level. Add if clauses to filter data without nesting everything in conditionals.
  • Readability is the goal. If a comprehension gets too complex, break it into smaller pieces or use function chains with descriptive function names.

Practice: Book Recommendations Expanded

Try writing a comprehension to:

  • Extract all unique authors from the books list
  • Find all movies with more than 10 characters in the title
  • Group recommendations by author (using a comprehension to build tuples)
  • Create a dict mapping authors to their movies
exercise.py
# Solution: Unique authors
unique_authors = list(set(
    author
    for book in books
    for author in book["authors"]
))

# Solution: Filter by movie title length
long_movie_titles = [
    movie["title"]
    for book in books
    for author in book["authors"]
    for movie in book_adaptations(author)
    if len(movie["title"]) > 10
]

# Solution: Group by author
by_author = {
    author: [
        movie["title"]
        for movie in book_adaptations(author)
    ]
    for author in unique_authors
}

print(by_author)
Output
{'Chiusano': [], 'Bjarnason': [], 'Tolkien': ['An Unexpected Journey', 'The Desolation of Smaug']}

The key insight is that comprehensions can express complex data transformations in a declarative way. Once you understand the order of for and if clauses, you can express almost any sequential transformation.