Marcus Goldfish wrote:
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

Reply via email to