Re: [Tutor] iterating over a changing list

2012-10-10 Thread Steven D'Aprano

On 11/10/12 08:49, eryksun wrote:


Also, generally avoid mutating a list while iterating over it.
listiterator is just incrementing an index, so modifying the size of
the list can produce nonsense (e.g. if you remove the current item,
the next item will be skipped). Instead, create an empty list and
append() to it.



If you absolutely have to modify the list you are iterating over,
iterate over it backwards:


# this doesn't work correctly
for i in range(len(mylist)):
x = mylist[i]
if x < 0:
del mylist[i]


# this does
for i in range(len(mylist)-1, -1, -1):
x = mylist[i]
if x < 0:
del mylist[i]


But really, don't do that either. Iterate over a copy, or make
a new list with the items you want. It's faster and easier.



--
Steven
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] iterating over a changing list

2012-10-10 Thread eryksun
On Wed, Oct 10, 2012 at 3:52 PM, Ed Owens  wrote:
>
> import string

Why are you importing "string"? Most string functions one would need
are methods of str/unicode. Sometimes "string" is still required,
however.

> def add_element(items, point):
> items = items[:point+1][:] + [['new']] + items[point+1:]
> return items

items[:point+1][:] creates a copy of part of the list with the slice
[:point+1], and then it copies the copy with the slice [:].

Redundant operations aside, this is returning a new list object.
That's not going to work since the loop iterator is bound to the
original list. You can't rebind a listiterator in the middle of a for
loop.

Also, generally avoid mutating a list while iterating over it.
listiterator is just incrementing an index, so modifying the size of
the list can produce nonsense (e.g. if you remove the current item,
the next item will be skipped). Instead, create an empty list and
append() to it.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] iterating over a changing list

2012-10-10 Thread Dave Angel
On 10/10/2012 03:52 PM, Ed Owens wrote:
> I'm trying to iterate over a list of elements, and make changes to the list
> in front of the element I'm currently working with.  I can update the list,
> but the  'for'  doesn't see the new element.  Here's the code:
>
> import string
>
> def add_element(items, point):
> items = items[:point+1][:] + [['new']] + items[point+1:]
> return items
This function doesn't change its input object at all, it's just creates
and returns a new one.
> def main():
> pass
>
> itmlst = [['a'],['b']]
> itmcntr = 0
>
> for itm in itmlst:
> cmd = ''
> while True:
> cmd = raw_input('break, add, print:')
> if cmd == 'break':
> break
> elif cmd == 'add':
> itmlst = add_element(itmlst,itmcntr)

Now you've created a brand new list, and bound it to the itemlst
variable, but you're still iterating over the original list.

> elif cmd == 'print':
> print 'current item:', itm
> else:
> print 'invalid'
> itmcntr += 1
> print 'finished with', itm, 'in', itmlst
> print len(itmlst), 'total items in list'
>
> If I provide the inputs: [print add print break print break] at the prompt,
> I get this output
>
> current item: ['a']
> current item: ['a']
> finished with ['a'] in [['a'], ['new'], ['b']]
> current item: ['b']
> finished with ['b'] in [['a'], ['new'], ['b']]
> 3 total items in list
>
> The new element got added, but it wasn't used in the iteration over the list
> of items. Other than setting up a counter and calling len() each loop, is
> there some way to have the changing list recognized within the for loop?
>
>

Usually, people have the reverse problem, one of seeming to stutter on
one of the items, or skipping one.  And the cure for either of those is
(as Mark tried to point out) to make a new list object to iterate over.

if you're sure you want to insert items in the original list that you're
looping on, then you need to change two places.  One is the
add_element() function needs to either use the insert() or an equivalent
set of slices.  use the insert() method, since it's easier.  And don't
bother returning the list, since you're not creating a new one. 
Convention is to either return a new object, or modify an object in
place, but not both.

The other place to change is the place where you call that function.  It
should NOT bind the variable to the return value, since it doesn't want
a new list object.

I haven't studied the rest of your code, as it didn't make much sense to
me.  But it's probably a simplified version of whatever you are trying
to do, so that should be fine.



-- 

DaveA

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] iterating over a changing list

2012-10-10 Thread Mark Lawrence

On 10/10/2012 20:52, Ed Owens wrote:

I'm trying to iterate over a list of elements, and make changes to the list
in front of the element I'm currently working with.  I can update the list,
but the  'for'  doesn't see the new element.  Here's the code:

import string

def add_element(items, point):
 items = items[:point+1][:] + [['new']] + items[point+1:]
 return items

def main():
 pass

itmlst = [['a'],['b']]
itmcntr = 0

for itm in itmlst:
 cmd = ''
 while True:
 cmd = raw_input('break, add, print:')
 if cmd == 'break':
 break
 elif cmd == 'add':
 itmlst = add_element(itmlst,itmcntr)
 elif cmd == 'print':
 print 'current item:', itm
 else:
 print 'invalid'
 itmcntr += 1
 print 'finished with', itm, 'in', itmlst
print len(itmlst), 'total items in list'

If I provide the inputs: [print add print break print break] at the prompt,
I get this output

current item: ['a']
current item: ['a']
finished with ['a'] in [['a'], ['new'], ['b']]
current item: ['b']
finished with ['b'] in [['a'], ['new'], ['b']]
3 total items in list

The new element got added, but it wasn't used in the iteration over the list
of items. Other than setting up a counter and calling len() each loop, is
there some way to have the changing list recognized within the for loop?

Thanks in advance for any help.

Ed



___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor



Usually handled by iterating over a copy of the list.

for itm in itmlst[:]:

--
Cheers.

Mark Lawrence.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor