Confused over Lists

Michael Chermside mcherm at destiny.com
Fri Aug 2 11:25:44 EDT 2002


 >     demoList = [1, 1, 2, 3, 4, 5]
 >     for num in demoList:
 >         if num == 1:
 >             demoList.remove(num)

The problem that you had in modifying a list while looping through it is 
a common one. The simplest and best solution has already been suggested, 
namely:

     demoList = [x for x in demoList if x != 1]

However, you also asked about this "magic counter", so I figured someone 
should answer that question for you.

When you write something like this:


     for x in foo:
         ...

what happens is that the interpreter calls "iter(foo)", to obtain an 
"iterator" from the foo object. (If the foo object is already an 
iterator, it just returns itself!). An iterator is simply an object 
which has a .next() method (and, for the above reason, a .__iter__() 
method returning itself). This .next() method gets called each time 
through the loop, and should return the items in the container one at a 
time. (It doesn't even have to be a container... there are iterators 
that produce "infinite" lists, iterators that calculate successive 
values of a function, iterators that go through files line-by-line... 
anything is allowed so long as ".next()" makes sense.) When it gets to 
the end, it raises a StopIteration exception.

Now, when you used the built-in list object in a for statement, it 
generated whatever kind of iterator that built-in list object uses. And 
the kind of iterator that the built-in list object uses is the 
bare-bones obvious one you could come up with in 10 seconds... it's got 
a counter to keep track of the list position, and that's it. There are 
no smarts about what to do if the list gets modified. There are no 
smarts to allow you access to this counter (so, although it isn't 
"magic", it's not particularly "useful").

But if you wanted those things, you could build them yourself! There's 
no reason that you HAVE to use the default (dumb-but-fast) iterator for 
lists... and it's really easy to build a smart one with the features you 
need! Here's the quick one I threw together:


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 )









More information about the Python-list mailing list