I'm trying to understand custom iterators in Python, and created the following toy class for sequence comparison, which seems to work:
class Foo(object): """A toy class to experiment with __eq__ and __iter__""" def __init__(self, listA, listB): self.head, self.tail = listA, listB def __iter__(self): return iter(self.head[:]+self.tail[:]) def __eq__(self, other): """Foo instances are equal if their respective subsequences, head, tail, are in the same order""" diff = [i for i, j in zip(self, other) if i != j] return len(diff) == 0
f1 = Foo( [1,2], ['a','b','c'] ) f2 = Foo( [1,2], ['a','b','c'] ) f3 = Foo( [1,2], ['a','b','d'] ) f1 == f2, f1 == f3
(True, False)
I'm not really sure if I'm implementing iter() correctly, for instance: should I make copies of the sublists? Should I try to implement my own next() method?
Advice, comments, and links to good tutorial web pages on iterators are appreciated!
Hi Marcus,
Here are some points I noticed -
1) I would probably change your __eq__ method to something like this:
def __eq__(self,other): return (self.head,self.tail) == (other.head,other.tail)
2) Or, if you really want your __eq__ method to use the iterator returned by __iter__(),
def __eq__(self, other): for i, j in map(None,self, other): if i != j: return False return True
One reason this might be a little better than your version is that using zip() as you have, f1 = Foo( [1,2], ['a','b','c']) and f2 = Foo( [1,2], ['a','b','c','q'] ) would compare equal. Another reason is that this version returns False as soon as it figures out the lists are not equal.
3) It's not a big deal, but instead of what you have in __iter__(), I might import itertools and then write something like
def __iter__(self): return itertools.chain(self.head,self.tail)
4) In this specific case, if you want to use the iterator returned by __iter__ in your __eq__ method, I think you should avoid using a next(). Here's a silly example why:
class Foo2(object): def __init__(self, listA,listB): self.head,self.tail = listA,listB self._current = -1 self._cache = None
def _fill_cache(self): """save some state""" if not self._cache: self._cache = self.head + self.tail
def __iter__(self): """the iterator is the iterable object itself""" return self
def next(self): """make the object an iterator""" self._fill_cache() self._current += 1
if self._current >= len(self._cache): raise StopIteration
return self._cache[self._current]
def __eq__(self, other): for i, j in map(None,self, other): if i != j: return False return True
if __name__ == '__main__': f1 = Foo2( [1,2], ['a','b','c']) f2 = Foo2( [1,2], ['a','b','c']) f3 = Foo2( [1,2], ['a','b','d']) print f1 == f2, f1 == f3, f1 == f3 # notice the output here
Good luck.
Rich
_______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor