On 02/09/2015 08:46 PM, Chris Angelico wrote:
On Tue, Feb 10, 2015 at 3:33 PM, Charles Hixson
<charleshi...@earthlink.net> wrote:
The proper version of the "hard way" is:

1) The __iter__ method of the iterable constructs a new iterator
instance and returns it.

2) The __iter__ method of the *iterator* simply returns itself.

3) The __next__ method of the iterator tracks the current value and
returns the next value. Note that the iterator should never store the
iterable's data internally, unless the iterable is immutable and the
calculation is trivial (e.g. a range object). Instead, it should
determine the next value by referring to its source iterable.
So if I'm understanding this correctly, I should implement as an internal
class within Grid something like:
     class GridIter(Iterator):
Apart from the fact that you shouldn't have to explicitly subclass
Iterator, yes. But this is the hard way to do things. The easy way is
to simply define an __iter__ method on your Grid which returns an
iterator - and one excellent form of iterator, for custom classes like
this, is a generator object. Your original code can slot happily in,
with one tiny change:

class Grid:
     blah blah

     def __iter__(self):
         for row in range(self._rows):
             for col in range(self._cols):
                 if self._grid[row][col]:
                     yield self._grid[row][col]

The only change is to remove the explicit StopIteration at the end;
once your generator function terminates, the generator object will
raise StopIteration forever afterward, without any help from you.
(Also, post-PEP479, the explicit raise will actually cause
RuntimeError, so it's not just superfluous, but actually a problem.)

But I'm guessing that your grid and rows are actually iterable
themselves. If they are, you can cut the code down to this:

     def __iter__(self):
         for row in self._grid:
             for cell in row:
                 if cell: yield cell

or a generator expression:

     def __iter__(self):
         return (cell for row in self._grid for cell in row if cell)

or itertools.chain and filter, if you so desired. As long as you
return an iterator, you're fine. This is far and away the easiest way
to make a class iterable.

ChrisA
Yes, rows and cols are lists, but I'm going to need to iterate through them more than once. I'd rather do without a included class, but if a properly formed iterator can only be cycled through once, and if I understand properly that means I can't use the "class instance is it's own iterator" form.
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to