|
3 | 3 | "qhelp.dtd"> |
4 | 4 | <qhelp> |
5 | 5 | <overview> |
6 | | -<p>The <code>__iter__</code> method of a class should return an iterator. |
| 6 | +<p>The <code>__iter__</code> method of a class should always return an iterator.</p> |
7 | 7 |
|
8 | | -Iteration in Python relies on this behavior and attempting to iterate over an |
9 | | -instance of a class with an incorrect <code>__iter__</code> method will raise a TypeError. |
10 | | -</p> |
| 8 | +<p>Iterators must implement both <code>__next__</code> and <code>__iter__</code> for Python 3, or both <code>next</code> and <code>__iter__</code> for Python 2. The <code>__iter__</code> method of the iterator must return the iterator object itself.</p> |
11 | 9 |
|
| 10 | +<p>Iteration in Python relies on this behavior and attempting to iterate over an instance of a class with an incorrect <code>__iter__</code> method can raise a <code>TypeError</code>. |
| 11 | +</p> |
12 | 12 |
|
13 | 13 | </overview> |
14 | 14 | <recommendation> |
15 | | -<p>Make the <code>__iter__</code> return a new iterator, either as an instance of |
16 | | -a separate class or as a generator.</p> |
17 | | - |
| 15 | +<p>Make sure the value returned by <code>__iter__</code> implements the full iterator protocol.</p> |
18 | 16 | </recommendation> |
19 | 17 | <example> |
20 | | -<p>In this example the <code>MyRange</code> class's <code>__iter__</code> method does not |
21 | | -return an iterator. This will cause the program to fail when anyone attempts |
22 | | -to use the iterator in a <code>for</code> loop or <code>in</code> statement. |
23 | | -</p> |
| 18 | +<p>In this example, we have implemented our own version of <code>range</code>, extending the normal functionality with the ability to skip some elements by using the <code>skip</code> method. However, the iterator <code>MyRangeIterator</code> does not fully implement the iterator protocol (namely it is missing <code>__iter__</code>).</p> |
24 | 19 |
|
25 | | -<p>The fixed version implements the <code>__iter__</code> method as a generator function.</p> |
| 20 | +<p>Iterating over the elements in the range seems to work on the surface, for example the code <code>x = sum(my_range)</code> gives the expected result. However, if we run <code>sum(iter(my_range))</code> we get a <code>TypeError: 'MyRangeIterator' object is not iterable</code>.</p> |
| 21 | + |
| 22 | +<p>If we try to skip some elements using our custom method, for example <code>y = sum(my_range.skip({6,9}))</code>, this also raises a <code>TypeError</code>.</p> |
| 23 | + |
| 24 | +<p>The fix is to implement the <code>__iter__</code> method in <code>MyRangeIterator</code>.</p> |
26 | 25 |
|
27 | 26 | <sample src="IterReturnsNonIterator.py" /> |
28 | 27 |
|
29 | 28 | </example> |
30 | 29 | <references> |
31 | 30 |
|
32 | | - <li>Python Language Reference: <a href="http://docs.python.org/2.7/reference/datamodel.html#object.__iter__">object.__iter__</a>.</li> |
33 | | - <li>Python Standard Library: <a href="http://docs.python.org/2/library/stdtypes.html#typeiter">Iterator Types</a>.</li> |
34 | | - |
| 31 | + <li>Python Language Reference: <a href="https://docs.python.org/3/reference/datamodel.html#object.__iter__">object.__iter__</a>.</li> |
| 32 | + <li>Python Standard Library: <a href="https://docs.python.org/3/library/stdtypes.html#typeiter">Iterator Types</a>.</li> |
35 | 33 |
|
36 | 34 | </references> |
37 | 35 | </qhelp> |
0 commit comments