[Tutor] In-place changes on loops

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Thu Feb 3 19:52:09 CET 2005



On Thu, 3 Feb 2005, Sandip Bhattacharya wrote:

> >    for x in string:
> >        if x in chars:
> >            string[i] = ''
>
> I just have a hangover from other languages, but I really wanted to know
> how Python handles iteration over a variable which is being changed
> within the loop itself. Is the "for" condition evaluated in every loop?

Hi Sandip,

Strings are immutable, so the above code won't work.  *grin*


But let's change it to what I think you were thinking of:

###
def lstrip(string, chars):
    scratchspace = list(string)       ## get a mutable list of characters
    for x in scratchspace:
        if x in chars:
            scratchspace[i] = ''
    return ''.join(scratchspace)
###

This works fine.  In-place list changes that don't modify list structure
will do ok.


When we do structural changes to a list that we're iterating across, then
we do have to be careful.  The Reference Manual does mention caution near
the bottom of:

    http://www.python.org/doc/ref/for.html


When we have an statement of the form:

### Pseudocode ###
for <var> in <some expresssion>:
    <body>
######

then Python does evaluate <some expression> just once.


However, what it gets back is an "iterator", an object that marches across
a sequence.  For example:

###
>>> message = list("this is a test")
>>> iterator = iter(message)
>>> iterator.next()
't'
>>> iterator.next()
'h'
>>> iterator.next()
'i'
>>> iterator.next()
's'
###

In effect, the for loop repeatedly calls next() on an iterator until it
exhausts itself.  The iterator doesn't try to be smart, so if we start
mutating the list that backs the iterator, we do risk skipping elements.


Let's try an experiment:

###
>>> message = list("testing")
>>> iterator = iter(message)
>>> iterator.next()
't'
>>> iterator.next()
'e'
>>> message.insert(0, "x")
>>> iterator.next()
'e'
>>> iterator.next()
's'
###


The 'insert()' shoves all the elements one place right.  But, blissfully
ignorant, the iterator doesn't shift itself, but instead stays in place.
So the next call to next() revisits that 'e' character.


Does this make sense?  Please feel free to ask more questions about this.



More information about the Tutor mailing list