Conditionals in generator expressions give you the same filtering and transformation power as list comprehensions — but with the memory efficiency and lazy evaluation of generators. You can add if clauses to include only items that meet a condition, or use conditional expressions (if/else) to yield different values based on a test — all while producing values one at a time on demand. In 2026, this pattern is essential for processing large or infinite streams, filtering data lazily, and building memory-efficient pipelines in data science, file handling, APIs, and streaming applications.
Here’s a complete, practical guide to conditionals in generator expressions: filtering with if, value transformation with if/else, real-world patterns, and modern best practices with type hints, readability, and safety.
The basic filtering form uses if at the end — only items that pass the condition are yielded by the generator.
numbers = range(10)
# Even squares only — generator yields one at a time
even_squares_gen = (num ** 2 for num in numbers if num % 2 == 0)
for square in even_squares_gen:
print(square) # 0, 4, 16, 36, 64
# Or consume directly — no full list created
print(sum(num ** 2 for num in numbers if num % 2 == 0)) # 120
Use conditional expressions (value_if_true if condition else value_if_false) to yield different values based on a test — instead of filtering out items.
# Even ? square, odd ? 0 (conditional expression)
even_or_zero = (num ** 2 if num % 2 == 0 else 0 for num in numbers)
print(list(even_or_zero)) # [0, 0, 4, 0, 16, 0, 36, 0, 64, 0]
Real-world pattern: lazy processing of large files or streams — conditionals filter or transform lines on the fly without loading everything into RAM.
# Sum only valid numeric lines from a huge log file
with open("huge_log.txt", "r", encoding="utf-8") as f:
total = sum(float(line.strip()) for line in f if line.strip().isdigit())
print(f"Total of valid numbers: {total}")
Another everyday use: conditional mapping and filtering in data pipelines — e.g., cleaning, transforming, and selecting records lazily.
raw_data = [" alice ", "BOB123", " charlie ", "david@email.com", "invalid!"]
# Clean emails lazily, skip invalid
valid_emails_gen = (email.strip().lower() for email in raw_data
if "@" in email and "." in email.split("@")[1])
for email in valid_emails_gen:
print(email) # alice, david@email.com
Best practices make generator expressions with conditionals safe, readable, and performant. Use parentheses () — not square brackets — to create generators; square brackets create lists (memory-heavy). Keep them simple — one expression, one if or simple if/else; if logic grows (>1–2 lines, nested conditions, try/except), switch to a generator function with yield. Add type hints for clarity — Generator[int, None, None] or Iterator[int] — improves readability and mypy checks. Prefer generator expressions over list comprehensions when passing to consumers (sum(), max(), etc.) — they avoid unnecessary list creation. Modern tip: use generator expressions with itertools (islice, takewhile, filterfalse) for advanced lazy filtering/slicing. In production, when iterating external data (files, APIs), wrap in try/except — handle bad items gracefully without crashing. Combine with enumerate() or zip() for indexed or parallel lazy iteration.
Conditionals in generator expressions turn simple iteration into filtered, transformed lazy streams — memory-efficient, scalable, and elegant. In 2026, use if for filtering, if/else for mapping, type hints for safety, and keep them readable. Master this pattern, and you’ll process large datasets, files, and streams with confidence and low memory footprint.
Next time you need to filter or transform data lazily — reach for a generator expression with a conditional. It’s Python’s cleanest way to say: “Compute and yield only what passes the test, one value at a time.”