On Fri, 18 Dec 2009 10:39:15 -0800, Carl Banks wrote: > It is true that Python iterators can't be used to mutate the underlying > structure--if there is actual underlying data structure--
An iterator is a protocol. So long as you have __iter__ and next (or __next__ in Python 3) methods, your class is an iterator. What you do in those methods are up to you. If you want to mutate the underlying data, you're free to do so. Just for chuckles, here's a quick-and-dirty iterator version of the Tower of Hanoi that mutates itself as needed. (I make no promise that it is either efficient or bug-free.) class Hanoi: def __init__(self, n=5): self.a = range(n, 0, -1) self.b = [] self.c = [] self.num = 0 def __str__(self): template = "Needle %d: %s" d = [template % (i, data) for (i,data) in enumerate([self.a, self.b, self.c])] return '\n'.join(d) + '\n' def __iter__(self): return self def next(self): if [] == self.a == self.c: raise StopIteration self.num += 1 if self.num % 2: # Odd-numbered move: move the smallest disk one # position clockwise. needle = self.find_smallest() nxt = "bca"["abc".find(needle)] else: # Even-numbered move: make the only legal move of the # next-smallest disk. needle, nxt = self.find_legal() self.move(needle, nxt) return str(self) def move(self, frm, to): """Move the top disk from needle `frm` to needle `to`.""" from_needle = getattr(self, frm) to_needle = getattr(self, to) to_needle.append(from_needle[-1]) del from_needle[-1] def find_smallest(self): for needle in 'abc': if 1 in getattr(self, needle): return needle def find_legal(self): """Find the only legal move not using the smallest disk.""" ignore = self.find_smallest() if ignore == 'a': disks = 'bc' elif ignore == 'b': disks = 'ac' else: disks = 'ab' left_needle = getattr(self, disks[0]) right_needle = getattr(self, disks[1]) if left_needle == []: return disks[1], disks[0] elif right_needle == []: return disks[0], disks[1] elif left_needle[-1] < right_needle[-1]: return disks[0], disks[1] else: return disks[1], disks[0] And in use: >>> p = Hanoi() >>> for state in p: ... print state ... Needle 0: [5, 4, 3, 2] Needle 1: [1] Needle 2: [] Needle 0: [5, 4, 3] Needle 1: [1] Needle 2: [2] [ rest of output skipped for brevity ] Python iterators don't generally mutate the thing they are iterating over, not because they can't, but because it's generally a bad idea to do so. In general, it really only makes sense as an optimization in situations where you have so much data that you can't afford to make a copy of it. For the occasional time where it does make sense, Python iterators are perfectly capable of doing so. -- Steven -- http://mail.python.org/mailman/listinfo/python-list