Python List Comprehensions: Filters, Conditions, Nested Loops and Readability

Python course module guide: Data structures and expressions

Python list comprehensions provide a compact way to build a new list from an iterable. They are excellent for small transformations and filters, but a shorter expression is not automatically a clearer expression.

What you will learn

  • Write transformation and filtering comprehensions.
  • Distinguish filters from conditional expressions.
  • Build dictionary and set comprehensions.
  • Know when a normal loop is more readable.

List comprehension syntax

The basic shape is [expression for item in iterable]. The expression describes the value placed in the new list, while the loop supplies each source item.

prices = [120, 250, 80, 400]
prices_with_tax = [round(price * 1.18, 2) for price in prices]

This produces a new list and leaves the original untouched. That makes the data flow easier to reason about than code that silently changes shared state. For the behavior of lists, tuples, sets and dictionaries, use the practical Python data structures guide.

Filter items with a trailing if

scores = [38, 72, 91, 44, 67]
passing_scores = [score for score in scores if score >= 50]

The trailing if decides whether an item enters the result. It does not provide an alternative value. Use it when unwanted rows should disappear.

Use a conditional expression for two outcomes

labels = ["pass" if score >= 50 else "retry" for score in scores]

Here every score produces one output. The if/else sits before the for because it is part of the expression. Beginners often confuse this with a filter; ask whether the result should keep every input or remove some inputs.

Call small functions from comprehensions

def normalize_name(value: str) -> str:
    return " ".join(value.strip().title().split())

raw_names = ["  anita  rao", "RAVI kumar", " meena "]
clean_names = [normalize_name(name) for name in raw_names]

A named function keeps the comprehension focused on data flow. The function can be tested separately and reused. Review Python parameters, return values and scope if the function boundary is unclear.

Dictionary and set comprehensions

students = [
    {"id": 101, "name": "Asha"},
    {"id": 102, "name": "Kiran"},
]

name_by_id = {student["id"]: student["name"] for student in students}
unique_words = {word.lower() for word in ["Python", "API", "python"]}

A dictionary comprehension needs both a key and a value. A set comprehension is useful when uniqueness is the goal. If duplicate keys occur in a dictionary comprehension, the later value wins, so validate source data when duplicates would indicate an error.

Nested loops: read from left to right

teams = [["Asha", "Ravi"], ["Meena", "John"]]
members = [name for team in teams for name in team]

The loop order matches the equivalent nested for statements. One extra level can be reasonable. Multiple conditions and several nested loops usually deserve a normal loop or helper function.

List comprehension vs generator expression

Square brackets build the full list immediately. Parentheses create a generator expression that yields items when requested. A list is useful when you need indexing, repeated passes or the complete result. A generator is useful for a one-pass pipeline or a large stream. Continue with Python iterators and generators to understand lazy evaluation.

Readability rules

  • Keep the expression and condition short enough to scan.
  • Move complex calculations into a named function.
  • Avoid side effects such as printing or updating external lists.
  • Do not reuse a comprehension only to appear clever.
  • Use meaningful names instead of single letters outside simple mathematics.

Practical cleaning example

records = ["101,Asha,82", "", "102,Ravi,74", "bad row"]

valid_rows = [row.split(",") for row in records if row.count(",") == 2]
result = [
    {"id": int(student_id), "name": name, "score": int(score)}
    for student_id, name, score in valid_rows
]

This demonstrates transformation and filtering, but production code should report bad rows instead of silently discarding them. For structured tabular analysis, the later pandas GroupBy and merge project provides stronger validation and reconciliation checks.

Practice checklist

Rewrite one simple loop as a list comprehension, one filter as a trailing if, and one two-outcome mapping with a conditional expression. Then reverse the exercise: convert a complicated comprehension into a readable loop. The ability to choose clarity is more important than memorizing syntax.

List comprehension FAQs

Are comprehensions always faster than loops?

They can be efficient for simple construction, but performance depends on the operation and data. Choose readability first and measure only when speed actually matters.

Can a comprehension modify an existing list?

Its normal purpose is to create a new collection. Using it only for side effects wastes the result and hides intent; use a regular loop for actions.

How long should a comprehension be?

There is no useful character limit. If a reader cannot identify the transformation, iteration and filter quickly, extract a function or write a loop.

New list does not always mean independent objects

The price example creates new numeric results. A different comprehension can create a new outer list while retaining references to mutable objects from the source. This matters when selecting dictionaries for editing: changing a selected dictionary can also change the original record because both lists point to that same dictionary.

The following experiment checks this distinction, the scope of the loop variable and the order of two nested clauses. Run it before predicting what a comprehension will do in a larger cleaning task.

name = "outside"
names = [name.upper() for name in ["asha", "ravi"]]
print(name, names)

rows = [{"score": 40}, {"score": 80}]
selected = [row for row in rows if row["score"] >= 50]
selected[0]["score"] = 90
print(rows)

pairs = [(team, level)
         for team in ["A", "B"]
         for level in [1, 2]]
print(pairs)

Observed output using Python 3.12.14:

outside ['ASHA', 'RAVI']
[{'score': 40}, {'score': 90}]
[('A', 1), ('A', 2), ('B', 1), ('B', 2)]

The outer name remains unchanged, while the selected score becomes ninety in the original rows as well. Filtering did not copy the dictionaries. If independent top-level dictionaries are required, construct new dictionaries deliberately. A shallow copy still shares any nested lists or dictionaries, so choose copying depth according to the actual data structure rather than assuming one copy operation isolates everything.

Read nested clauses as the loop you would write

The pairs appear as A-one, A-two, B-one and B-two. The first clause chooses a team; the next clause visits every level for that team. Swapping the clauses changes the output order. Adding a trailing condition filters combinations after the relevant loop variables have been bound.

Distinguish flattening from a Cartesian product. In the earlier team-members example, the inner iterable belongs to the current outer team. In the pairs example, every team combines with every level. A five-by-six product creates thirty candidate combinations before any filtering. For large inputs, a compact expression can hide a substantial amount of work.

When the expected output order matters, include that order in the test. Comparing sets would discard ordering evidence and could hide a mistake. Test an empty outer collection and an empty inner collection as well; both can change the number of results without producing an exception.

Comprehension scope is not a general protection from side effects

Python keeps the ordinary comprehension loop target from overwriting an enclosing variable of the same name, as the demonstration shows. However, functions called by the expression can still change shared state, and selected objects can still be shared. Scope controls name binding; it does not make every operation inside the expression pure.

Another subtlety appears when a comprehension creates functions that refer to its loop variable. Those functions can look up the same final value later rather than remembering a separate value from each iteration. If you need a captured value per function, use an explicit binding approach and test each generated function. For beginners, a named helper is often easier to review than a list of nested lambdas.

Handle validation where the error can be explained

The earlier comma-count example is intentionally small. Two commas do not guarantee that the identifier and score can be converted to integers, and splitting on commas is not a full CSV parser for quoted fields. If a conversion raises an exception, the comprehension stops; it does not automatically skip the bad row and continue.

For a teaching exercise, add an input such as a three-field row whose score contains letters. Decide whether the contract should reject the whole batch or collect a row-level error and continue. A normal loop with a small parsing function makes this policy visible and can retain the original row number. Filtering invalid data silently should be an intentional, documented choice.

When should I avoid compressing the loop?

Use the loop when each item needs several checks, separate exception handling, multiple outputs or clear diagnostic logging. Compact syntax is useful only when the data flow stays obvious.

What should the practice review compare?

Check values, result order, source mutation, duplicates and invalid input. Then explain whether the output holds new values or references to existing objects. That explanation demonstrates understanding beyond producing the expected list once.

Further official documentation: Python comprehension evaluation and scope; Python shallow and deep copying; Python FAQ on functions capturing loop variables.

Official reference: Python tutorial on list comprehensions.

Learn Python with guided practice in Visakhapatnam

These concepts become useful when you apply them in exercises, assignments and reviewed projects. Explore Python training in Vizag for the complete curriculum, classroom and online learning options, and current batch details.

Python module learning path

Continue through the related practical guides in this course-module series:

  1. Python match-case pattern matching
  2. Python list comprehensions
  3. Python iterators and generators
  4. Python decorators
  5. Python context managers
  6. Python custom exceptions
  7. pytest unit testing for beginners
  8. Python API pagination and retries
  9. pandas GroupBy and merge
  10. Flask REST API with SQLite

Leave a Comment

Your email address will not be published. Required fields are marked *