|
33 | 33 | In this chapter, we will investigate those that we did not yet already discuss in \cref{sec:enumOverSequences,sec:collections}. |
34 | 34 | % |
35 | 35 | \hsection{\texttt{Iterable}s and \texttt{Iterator}s}% |
| 36 | +\label{sec:iterable}% |
36 | 37 | % |
37 | 38 | \begin{figure}% |
38 | 39 | \centering% |
|
60 | 61 | This distinction is necessary because we want to allow some objects to be iterated over multiple times.% |
61 | 62 | \end{sloppypar}% |
62 | 63 | % |
63 | | -Let's say you have the \pythonilIdx{list} \pythonil{x = [1, 2, 3]}. |
| 64 | +Let's say you have the list \pythonil{x = ["a", "b", "c"]}, as in \cref{fig:iterateOverListAndRange:list}. |
64 | 65 | We can use this list~\pythonil{x} in a \pythonil{for xi in x}-kind of loop arbitrarily often. |
65 | | -The list is an \pythonilIdx{Iterable} object. |
66 | | -Every time we do so, an \pythonilIdx{Iterator} instance is created internally by (doing something like) invoking~\pythonil{y = iter(x)}\pythonIdx{iter}. |
| 66 | +\pythonil{x} is an instance of \pythonilIdx{list} and every list is also an~\pythonilIdx{Iterable}\pythonIdx{typing.Iterable}. |
| 67 | +Every time we do loop over \pythonil{x}, an \pythonilIdx{Iterator} instance is created internally by (doing something like) invoking~\pythonil{y = iter(x)}\pythonIdx{iter}. |
67 | 68 | In principle, this \pythonilIdx{Iterator} object only has to remember its current position in the list, allowing us to query the next item by invoking~\pythonil{next(y)}\pythonIdx{next}. |
68 | | -The \pythonilIdx{for}-loop basically does this internally.% |
| 69 | +The \pythonilIdx{for}-loop basically does this internally. |
| 70 | +However, we can also do it \inQuotes{by hand.} |
| 71 | +In \cref{fig:iterateOverListAndRange:list}, we perform \pythonil{u = iter(x)} and \pythonil{v = iter(x)}. |
| 72 | +This creates two independent \pythonilsIdx{Iterator}, which we can use to step over the list separately. |
| 73 | +Invoking \pythonil{next(u)}\pythonIdx{next} will yield the first element of the list~\pythonil{x}, namely~\pythonil{"a"}. |
| 74 | +Calling \pythonil{next(u)}\pythonIdx{next} again gives us the second element, that is~\pythonil{"b"}. |
| 75 | +If we now call \pythonil{next(v)}\pythonIdx{next}, i.e., apply~\pythonilIdx{next} to the second, independent \pythonilIdx{Iterator}, we again obtain the first element~(\pythonil{"a"}). |
| 76 | + |
| 77 | +This shows us why there is a distinction between \pythonilIdx{Iterable} and \pythonilIdx{Iterator}. |
| 78 | +The former is the object that holds or can generate the data sequence. |
| 79 | +The latter marks one independent iteration over that sequence. |
| 80 | + |
| 81 | +The third invocation of \pythonil{next(u)}\pythonIdx{next} gives us~\pythonil{"c"}, the third and last element of~\pythonil{x}. |
| 82 | +If we now call \pythonil{next(u)}\pythonIdx{next} a fourth time, something interesting happens: |
| 83 | +A \pythonilIdx{StopIteration} is raised\pythonIdx{raise}. |
| 84 | +This is not an error in the strict sense. |
| 85 | +This instead is how the end of an iteration sequence is signaled. |
| 86 | +A \pythonilIdx{for}~loop will, for instance, stop when it encounters this exception. |
| 87 | + |
| 88 | +\Cref{fig:iterateOverListAndRange:range} shows us that \pythonilsIdx{range} have the exactly same behavior as \pythonilsIdx{list} with respect to iteration. |
| 89 | +And they should, of course, like every other object implementing the~\pythonilIdx{Iterable} functionality. |
| 90 | +As a result, the \pythonil{for y in x}-type of loops can be applied to any \pythonilIdx{Iterable} or \pythonilIdx{Iterator} instance~\pythonil{x}.% |
69 | 91 | % |
70 | 92 | \endhsection% |
71 | 93 | % |
|
0 commit comments