Charles Hixson wrote: > I'm trying to write a correct iteration over a doubly indexed container, > and what I've got so far is: def __next__ (self): > for row in range(self._rows): > for col in range(self._cols): > if self._grid[row][col]: > yield self._grid[row][col] > #end if > #end for col > #end for row > raise StopIteration
That's wrong, you don't use yield in __next__ as that will turn it into a generator function. Here is a simplified example demonstrating the problem: py> class X(object): ... def __next__(self): ... yield 1 ... yield 2 ... py> x = X() py> next(x) <generator object __next__ at 0xb7b0e504> py> next(x) <generator object __next__ at 0xb7b0e52c> py> next(x) <generator object __next__ at 0xb7b0e504> py> next(x) <generator object __next__ at 0xb7b0e52c> The way you write iterators is like this: Method 1 (the hard way): - Give your class an __iter__ method which simply returns self: def __iter__(self): return self - Give your class a __next__ method (`next` in Python 2) which *returns* a value. You will need to track which value to return yourself. It must raise StopIteration when there are no more values to return. Don't use yield. def __next__(self): value = self.value if value is None: raise StopIteration self.value = self.calculate_the_next_value() return value Your class is itself an iterator. Method 2 (the easy way): - Don't write a __next__ method at all. - Give your class an __iter__ method which returns an iterator. E.g.: def __iter__(self): return iter(self.values) In this case, your class is not itself an iterator, but it is iterable: calling iter(myinstance) will return an iterator, which is enough. If you don't have a convenient collection of values to return, you can conveniently use a generator, yielding values you want and just falling off the end (or returning) when you are done. E.g.: def __iter__(self): while self.value is not None: yield self.value self.calculate_the_next_value() In your case, it looks to me that what you need is something like: 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] which is probably better written as: # untested -- I may have the row/col order backwards def __iter__(self): for column in self._grid: for item in column: if value: yield value As a general rule, Python is not Fortran. If you find yourself wanting to write code that iterates over an index, then indexes into a list or array, 99.9% of the time you are better off just iterating over the list or array directly: # not this! for i in range(len(mylist)): value = mylist[i] print(value) # instead use this for value in mylist: print(value) If you need both the index and the list item, use enumerate: for i, value in enumerate(mylist): print(i, value) -- Steven -- https://mail.python.org/mailman/listinfo/python-list