Skip to main content
Writing “Pythonic” code means leveraging Python’s unique features to make programs clean, concise, and highly expressive. One of the clearest demonstrations of the Pythonic style is comprehensions — a compact syntax for transforming and filtering collections that replaces verbose loops with a single readable line. This page also covers iterators (the protocol that makes for loops work), generators (memory-efficient value producers), and context managers (automatic resource clean-up).

Comprehensions

Comprehensions provide a concise way to build new collections from existing ones.

List Comprehensions

Dictionary Comprehensions

Set Comprehensions

Set comprehensions automatically deduplicate results:

Iterators

An iterator is an object that produces one value at a time. It must implement two methods:
  1. __iter__() — returns the iterator object itself.
  2. __next__() — returns the next value; raises StopIteration when exhausted.
Every Python for loop calls iter() on the target, then repeatedly calls next() on the resulting iterator until StopIteration is raised.

Generators

Generators are a simpler way to create iterators using the yield keyword. A generator function pauses at each yield, remembers its state, and resumes on the next call — producing one value at a time rather than storing them all in memory.

Generator Functions

Generators for Large Datasets

Generator Expressions

Wrapped in () instead of [], generator expressions compute values lazily — only when requested:
Use generator expressions when working with large datasets or streams where you only need one element at a time. Passing a generator directly to sum(), max(), or min() is both memory-efficient and readable:

Generators vs. Lists

Context Managers

Context managers manage resources by guaranteeing that set-up and clean-up code always runs, even if an exception occurs.

The with Statement

The most familiar context manager opens files safely:

Creating Custom Context Managers

Use the @contextmanager decorator from contextlib for a concise generator-based approach:
Code before yield runs at the start of the with block. Code after yield (or in finally) runs when the block exits — whether normally or due to an exception.