faqts : Computers : Programming : Languages : Python : Language and Syntax : Itterators and Generators

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

Entry

How can I make an iterator for lists with features like advance/regress or failfast?
How can I advance/regress in a for loop?

Aug 2nd, 2002 08:56
Michael Chermside,


Suppose you have a loop like this:
    li = [1,1,2,3,3,4]
    for item in li:
        <code here>
One of the things that you CAN'T do is to back up or skip forward.
(Actually, "continue" can be used to skip forward by 1 item, but only
one.) Another thing that you SHOULDN'T do is to modify the list in the
for loop... all kinds of messy things could happen (try remove()'ing
items and see what it does).
The traditional solution to this is to break down and use index arithmatic.
    li = [1,1,2,3,3,4]
    for index in range(len(li)):
        <code here>
But sometimes this just isn't as elegant (although at least it's
simple). So another approach would be to create an iterator which has
the abilities you want already built in.
The following iterator will allow you to advance or regress through the
list, and it performs a very basic level of testing for list
modification (taking a fail-fast approach). It is used like this:
    li = [1,1,2,3,3,4]
    for item in MovableListIterator(li):
        <code>
After the code, you'll find test code (always a good idea of course!).
This has been tested and works well with Python 2.2.
class MovableListIterator:
    """This is an iterator for lists which allows one to advance
    or regress the position in the list. It also will raise an
    exception if the list is lengthened or shortened."""
    def __init__(self, myList):
        self.myList = myList
        self.originalLength = len(myList)
        self.nextIndex = 0
    def __iter__(self):
        return self
    class ListModifiedException(Exception):
        pass
    def next(self):
        if len(self.myList) != self.originalLength:
            raise MovableListIterator.ListModifiedException
        index = self.nextIndex
        if index >= self.originalLength:
            raise StopIteration
        self.nextIndex += 1
        return self.myList[index]
    def advance(self, places=1):
        self.nextIndex += places
    def regress(self, places=1):
        self.nextIndex -= places
import unittest
class TestMovableListIterator(unittest.TestCase):
    def test_create(self):
        li = list('abcde')
        i = MovableListIterator(li)
    def test_basic_loop(self):
        li = 'abcde'
        self.assertEqual( list(li), [x for x in MovableListIterator(li)] )
    def test_advance(self):
        li = 'abcdef'
        result = []
        i = MovableListIterator(li)
        result.append( i.next() )
        i.advance()
        result.append( i.next() )
        i.advance(2)
        result.append( i.next() )
        self.assertEqual( list('acf'), result )
        self.assertRaises( StopIteration, i.next )
    def test_regress(self):
        li = 'abc'
        result = []
        i = MovableListIterator(li)
        result.append( i.next() )
        i.regress()
        result.append( i.next() )
        result.append( i.next() )
        result.append( i.next() )
        i.regress(2)
        result.append( i.next() )
        result.append( i.next() )
        self.assertEqual( list('aabcbc'), result )
        self.assertRaises( StopIteration, i.next )
    def test_shrink_throws(self):
        li = [1,2,3,4]
        i = MovableListIterator(li)
        li.pop()
        self.assertRaises( MovableListIterator.ListModifiedException,
                           i.next )
    def test_grow_throws(self):
        li = [1,2,3,4]
        i = MovableListIterator(li)
        li.append(5)
        self.assertRaises( MovableListIterator.ListModifiedException,
                           i.next )