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 TrueOne 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 StopIterationreturn self._cache[self._current]
def __eq__(self, other):
for i, j in map(None,self, other):
if i != j:
return False
return Trueif __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 hereGood luck.
Rich
_______________________________________________ Tutor maillist - [email protected] http://mail.python.org/mailman/listinfo/tutor
