You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,10 +22,12 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0
22
22
- Markdown-backed example sources with `:::program` and `:::cell` blocks.
23
23
- Example source verifier, formatter, embedded-source build step, and golden parity check.
24
24
- GitHub Actions verification workflow.
25
+
-`iterators` and `generator-expressions` examples to make the Iteration arc explicit.
25
26
26
27
### Changed
27
28
28
29
- Example walkthroughs now pair prose, source fragments, and output evidence.
30
+
- Iteration examples now frame Python iteration as a value-stream protocol: producers, consumers, eager containers, lazy streams, and one-pass iterators.
29
31
- Read-only code highlighting is handled by Shiki; editable code highlighting is handled by CodeMirror.
30
32
- Prototype routes under `/layout-options/*` bypass the Worker Cache API and return `Cache-Control: no-store`.
31
33
- Static assets use immutable cache headers only on fingerprinted filenames.
Copy file name to clipboardExpand all lines: docs/example-quality-rubric.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,6 +22,7 @@ Release gates outside the score:
22
22
- page layout remains restrained and readable
23
23
- examples verify under the configured Python version
24
24
- generated embedded source and asset manifests are up to date
25
+
- iteration examples identify what produces values, what consumes values, whether values are stored or streamed, and whether the stream is reusable or one-pass
25
26
26
27
Quality bands:
27
28
@@ -44,6 +45,8 @@ Flag these during review even when the code is correct:
44
45
- Data is purely toy-shaped when realistic small data would clarify the purpose.
45
46
- Notes repeat the prose instead of adding practical guidance.
46
47
- The program shows valid syntax but not when or why to use it.
48
+
- An iteration example uses a lazy object but does not show when values are consumed.
49
+
- An iteration example blurs eager containers with one-pass streams.
47
50
48
51
## Strengthening checklist
49
52
@@ -55,3 +58,4 @@ Before publishing or substantially editing an example, ask:
55
58
4. Does the example use small realistic data?
56
59
5. Is there a contrast readers commonly need to avoid misuse?
57
60
6. Would an explicit loop, named function, or mutation-vs-copy contrast make the idiom clearer?
61
+
7. For iteration examples, what produces values, what consumes them, and are they stored eagerly or streamed lazily?
Comprehensions are expression forms for building collections from iterables. Read them from left to right: produce this value, for each item, optionally only when a condition is true.
9
+
Comprehensions are expression forms for building concrete collections from iterables. Read them from left to right: produce this value, for each item, optionally only when a condition is true.
10
10
11
11
They are best for direct transformations where the expression is still easy to scan. When the work needs several statements or names, an explicit loop is usually clearer.
12
12
13
-
Comprehensions can build lists, dictionaries, and sets. Prefer them when the output shape is obvious from the expression.
13
+
List, dictionary, and set comprehensions are eager: they build collections immediately. Generator expressions use similar syntax to stream values later and are covered in the Iteration section.
14
14
15
15
:::program
16
16
```python
@@ -71,5 +71,6 @@ print(unique_scores)
71
71
:::note
72
72
- The left side says what to produce; the `for` clause says where values come from.
73
73
- Use an `if` clause for simple filters.
74
+
- List, dict, and set comprehensions build concrete collections immediately.
74
75
- Switch to a loop when the transformation needs multiple steps or explanations.
Generator expressions look like list comprehensions with parentheses, but they produce an iterator instead of building a concrete collection immediately.
10
+
11
+
Use them when a consumer such as `sum()`, `any()`, or a `for` loop can use values one at a time. This keeps the transformation close to the consumer and avoids storing intermediate lists.
12
+
13
+
Like other iterators, a generator expression is consumed as values are requested. Create a new generator expression when you need another pass.
14
+
15
+
:::program
16
+
```python
17
+
numbers = [1, 2, 3, 4]
18
+
list_squares = [number * number for number in numbers]
19
+
print(list_squares)
20
+
21
+
stream_squares = (number * number for number in numbers)
22
+
print(next(stream_squares))
23
+
print(next(stream_squares))
24
+
print(list(stream_squares))
25
+
26
+
print(sum(number * number for number in numbers))
27
+
```
28
+
:::
29
+
30
+
:::cell
31
+
A list comprehension is eager: it builds a list immediately. That is useful when you need to store or reuse the results.
32
+
33
+
```python
34
+
numbers = [1, 2, 3, 4]
35
+
list_squares = [number * number for number in numbers]
36
+
print(list_squares)
37
+
```
38
+
39
+
```output
40
+
[1, 4, 9, 16]
41
+
```
42
+
:::
43
+
44
+
:::cell
45
+
A generator expression is lazy: it creates an iterator that produces values as they are consumed. After two `next()` calls, only the remaining squares are left.
46
+
47
+
```python
48
+
stream_squares = (number * number for number in numbers)
49
+
print(next(stream_squares))
50
+
print(next(stream_squares))
51
+
print(list(stream_squares))
52
+
```
53
+
54
+
```output
55
+
1
56
+
4
57
+
[9, 16]
58
+
```
59
+
:::
60
+
61
+
:::cell
62
+
Generator expressions are common inside reducing functions. When a generator expression is the only argument, the extra parentheses can be omitted.
63
+
64
+
```python
65
+
print(sum(number * number for number in numbers))
66
+
```
67
+
68
+
```output
69
+
30
70
+
```
71
+
:::
72
+
73
+
:::note
74
+
- List, dict, and set comprehensions build concrete collections.
75
+
- Generator expressions produce one-pass iterators.
76
+
- Use generator expressions when the consumer can process values one at a time.
Copy file name to clipboardExpand all lines: src/example_sources/generators.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,11 +2,11 @@
2
2
slug = "generators"
3
3
title = "Generators"
4
4
section = "Iteration"
5
-
summary = "yield produces a lazy sequence of values."
5
+
summary = "yield creates an iterator that produces values on demand."
6
6
doc_path = "/tutorial/classes.html#generators"
7
7
+++
8
8
9
-
A generator function uses `yield`to produce values lazily. Calling the function returns an iterator; the body runs only as values are requested.
9
+
A generator function is a convenient way to write your own iterator. `yield` produces one value, pauses the function, and resumes when the next value is requested.
10
10
11
11
Generators are useful for pipelines, large inputs, and infinite sequences because they avoid building an entire collection in memory.
12
12
@@ -64,7 +64,7 @@ for value in countdown(3):
64
64
:::
65
65
66
66
:::note
67
-
- Generator functions return iterators.
67
+
- Generator functions are a concise way to create custom iterators.
The for statement works with any iterable object: lists, strings, dictionaries, generators, files, and many standard-library helpers. This makes iteration a central Python protocol rather than a special case for arrays.
9
+
Python's `for` statement consumes values from any iterable object: lists, strings, dictionaries, ranges, generators, files, and many standard-library helpers.
10
10
11
-
Use enumerate() when you need positions and values together, and dict.items() when you need keys and values. These helpers express intent better than manual indexing.
11
+
This makes iteration a value-stream protocol rather than a special case for arrays. The producer decides how values are made, and the loop consumes them one at a time.
12
12
13
-
Python's for statement consumes iterables through the iterator protocol. Prefer enumerate() over range(len(...)) when you need an index.
13
+
Use `enumerate()` when you need positions and values together, and `dict.items()`when you need keys and values. These helpers express intent better than manual indexing.
14
14
15
15
:::program
16
16
```python
17
17
names = ["Ada", "Grace", "Guido"]
18
18
19
-
# Iterate over values directly.
20
19
for name in names:
21
20
print(name)
22
21
23
-
# enumerate adds a counter without manual indexing.
24
22
for index, name inenumerate(names):
25
23
print(index, name)
26
24
27
25
scores = {"Ada": 10, "Grace": 9}
28
-
29
-
# items yields key/value pairs from a dictionary.
30
26
for name, score in scores.items():
31
27
print(name, score)
32
28
```
33
29
:::
34
30
35
31
:::cell
36
-
Start with an ordinary list. A list is iterable, so a for loop can ask it for one value at a time.
32
+
Start with an ordinary list. A list stores values, and a `for` loop asks it for one value at a time.
37
33
38
34
When you only need the values, iterate over the collection directly. There is no index variable because the loop body does not need one.
39
35
40
36
```python
41
37
names = ["Ada", "Grace", "Guido"]
42
38
43
-
# Iterate over values directly.
44
39
for name in names:
45
40
print(name)
46
41
```
@@ -53,10 +48,9 @@ Guido
53
48
:::
54
49
55
50
:::cell
56
-
When you need both a position and a value, use enumerate(). It keeps the counter tied to iteration without manual indexing.
51
+
When you need both a position and a value, use `enumerate()`. It produces index/value pairs without manual indexing.
57
52
58
53
```python
59
-
# enumerate adds a counter without manual indexing.
60
54
for index, name inenumerate(names):
61
55
print(index, name)
62
56
```
@@ -69,12 +63,10 @@ for index, name in enumerate(names):
69
63
:::
70
64
71
65
:::cell
72
-
Dictionaries are iterable too, but dict.items() is the clearest way to say that the loop needs keys and values together.
66
+
Dictionaries are iterable too, but `dict.items()` is the clearest way to say that the loop needs keys and values together.
73
67
74
68
```python
75
69
scores = {"Ada": 10, "Grace": 9}
76
-
77
-
# items yields key/value pairs from a dictionary.
78
70
for name, score in scores.items():
79
71
print(name, score)
80
72
```
@@ -86,6 +78,7 @@ Grace 9
86
78
:::
87
79
88
80
:::note
89
-
- Python's for statement consumes iterables through the iterator protocol.
90
-
- Prefer enumerate() over range(len(...)) when you need an index.
81
+
- A `for` loop consumes values from an iterable.
82
+
- Different producers can feed the same loop protocol.
83
+
- Prefer `enumerate()` over `range(len(...))` when you need an index.
An iterable is an object that can produce values for a loop. An iterator is the object that remembers where that production currently is.
10
+
11
+
`iter()` asks an iterable for an iterator, and `next()` consumes one value from that iterator. A `for` loop performs those steps for you until the iterator is exhausted.
12
+
13
+
This is the core value-stream protocol in Python: one object produces values, another piece of code consumes them, and many streams are one-pass.
14
+
15
+
:::program
16
+
```python
17
+
names = ["Ada", "Grace", "Guido"]
18
+
iterator =iter(names)
19
+
print(next(iterator))
20
+
print(next(iterator))
21
+
22
+
for name in iterator:
23
+
print(name)
24
+
25
+
again =iter(names)
26
+
print(next(again))
27
+
```
28
+
:::
29
+
30
+
:::cell
31
+
`iter()` asks an iterable for an iterator. `next()` consumes one value and advances the iterator's position.
32
+
33
+
```python
34
+
names = ["Ada", "Grace", "Guido"]
35
+
iterator =iter(names)
36
+
print(next(iterator))
37
+
print(next(iterator))
38
+
```
39
+
40
+
```output
41
+
Ada
42
+
Grace
43
+
```
44
+
:::
45
+
46
+
:::cell
47
+
A `for` loop consumes the same iterator protocol. Because two values were already consumed, the loop sees only the remaining value.
48
+
49
+
```python
50
+
for name in iterator:
51
+
print(name)
52
+
```
53
+
54
+
```output
55
+
Guido
56
+
```
57
+
:::
58
+
59
+
:::cell
60
+
The list itself is reusable. Asking it for a fresh iterator starts a new pass over the same stored values.
61
+
62
+
```python
63
+
again =iter(names)
64
+
print(next(again))
65
+
```
66
+
67
+
```output
68
+
Ada
69
+
```
70
+
:::
71
+
72
+
:::note
73
+
- Iterables produce iterators; iterators produce values.
74
+
-`next()` consumes one value from an iterator.
75
+
- Many iterators are one-pass even when the original collection is reusable.
The `itertools` module contains iterator building blocks for combining, slicing, grouping, and repeating streams of values. These tools pair naturally with `for` loops and generators.
9
+
The `itertools` module contains tools for composing iterator streams: combining, slicing, grouping, and repeating values without changing the consumer protocol.
10
10
11
11
Many `itertools` functions are lazy. They describe work to do later instead of building a list immediately, so helpers such as `islice()` are useful when taking a finite window.
12
12
13
-
Iterator pipelines let each step stay small. Convert to `list()` only at the edge when you need to display, store, or reuse the results.
13
+
Iterator pipelines let each step stay small: one object produces values, another transforms them, and a final consumer such as `list()`or a loop pulls values through the pipeline.
14
14
15
15
:::program
16
16
```python
@@ -71,6 +71,7 @@ print(list(high_scores))
71
71
:::
72
72
73
73
:::note
74
+
-`itertools` composes producer and transformer streams.
74
75
- Iterator pipelines avoid building intermediate lists.
75
76
- Use `islice()` to take a finite piece from an infinite iterator.
76
77
- Convert to a list only when you need concrete results.
0 commit comments