Reputation: 25
In Python...
I have a nested list defined in a class as:
self.env = [[ None for i in range(columnCount)] for j in range(rowCount) ]
For the purpose of this question, lets assume this nested list contained values of:
[None, None, None, None]
[None, None, None, None]
[None, None, 0, None]
[None, None, None, None]
I need to be able to leverage the iter and next() methods to go through this list in column major order (row0,col0 row1,col0, row2,col0...).
The intent is for it to send the value located in that grid location to another function to get processed.
So far I have the iter function defined as:
def __iter__(self):
return iter(self.env)
Then I have the next function defined as:
def next(self):
self.currentRow += 1
self.currentColumn += 1
if ( (self.currentRow < self.getRowCount()) and (self.currentColumn < self.getColumnCount()) ):
return self.env[self.currentRow][self.currentColumn]
else:
raise StopIteration
The problem I am having is that it seems the iter function is return each row as a list, but it is not making a call to the next method to further process that list.
My objective is to get print the values at each grid location invdividually... as in:
None None ... None 0 None ... None
Can someone elaborate on what it is I am missing in order to accomplish this?
Upvotes: 1
Views: 2218
Reputation: 91039
You might be approaching in a much too complicated way.
I would solve this problem on this way:
Take another example - yours is symmetrical to transposing, so you don't see the effect of doing wrong. Let's take
x = [[1, 2, 3, 4], [5, 6, 7, 8]]
for now.
Use zip()
or (for big lists) maybe better itertools.izip()
for transposing it, and remove one level:
y = [j for i in itertools.izip(*x) for j in i]
Here, izip()
iterates over all given lists and combines their 1st, 2nd, ... elements to produce tuples (1, 5), (2, 6)
and so on. i
iterates over these tuples, j
over their items. So y
is [1, 5, 2, 6, 3, 7, 4, 8]
.
Now, if you want your class instances to be iterable in that way, you can go several approaches:
You can __iter__()
be a generator function or return an iterator otherwise, e. g.
def __iter__(self): return (j for i in itertools.izip(*self.env) for j in i)
def __iter__(self):
for i in itertools.izip(*self.env):
for j in i: yield i
def __iter__(self): return iter([j for i in itertools.izip(*self.env) for j in i])
etc. This way, your object is iterable, but not an iterator.
You can, as you intended, let your object be its own iterator and let __iter__()
return self
and implement a next()
. I would object to this, however, as
once exhausted, you can (and should) not restart this iterator, as you can read here:
[...] once an iterator’s next() method raises StopIteration, it will continue to do so on subsequent calls. Implementations that do not obey this property are deemed broken. (This constraint was added in Python 2.3; in Python 2.2, various iterators are broken according to this rule.)
So you would need a new object for each iteration.
You can even do funny stuff with __getitem__()
, as this is a "last ressort approach" of Python to make an object iterable if it doesn't provide an iterator itself, as proposed by PEP234:
def __getitem__(self, index):
if index >= len(self): raise IndexError
# do a complicated calculation to map from this single index to your `self.env`
def __len__(self): # not needed, but handy
return sum(len(i) for i in self.env)
Upvotes: 2
Reputation: 97591
def __iter__(self):
return (self.env[j][i] for i in range(columnCount)
for j in range(rowCount))
Might need to swap I and j
Upvotes: 0
Reputation: 9533
Your __iter__
function is returning iter(self.env)
, which returns the iterator for self.env. If you want your next method to be called, __iter__
, you should return self
. But there are other issues in your implementation, for example, your iterator won't work more than once.
There are easier ways to achieve this. You can write a Generator Expression/List Comprehensions to achieve this (see @Eric's answer). You can also write a generator method. E.g.:
def iter_column_row():
for i in range(self.getColumnCount()):
for j in range(self.getRowCount()):
yield self.env[j][i]
Upvotes: 1