Mailing List Archive

newbie idiom question
Something I keep getting tripped up about is that objects being
iterated in "for" statements cannot be modified.

I keep trying to do this:

>>> eggs = [1,2,3]
>>> for spam in eggs:
... spam = 'cooked'
...
>>> eggs
[1, 2, 3]
>>>

The tutorial says this:

If you need to modify the list you are iterating over, e.g., duplicate
selected items, you must iterate over a copy. The slice notation makes
this particularly convenient:

>>> for x in a[:]: # make a slice copy of the entire list
... if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']

Understood, but what if you want to modify each element in a list?
What's the best way to do this in terms of speed and elegance? I guess
I just haven't seen a good example of this yet in Python

What I'm unfortunately used to is this, in Perl:

@eggs = (1,2,3);
foreach $spam (@eggs) {
$spam = 'cooked';
}
print "@eggs";
>>> cooked cooked cooked

Thanks,
Alex Rice
newbie idiom question [ In reply to ]
Alex Rice <alex@mindlube.com> writes:

> Something I keep getting tripped up about is that objects being
> iterated in "for" statements cannot be modified.
>
> I keep trying to do this:
>
> >>> eggs = [1,2,3]
> >>> for spam in eggs:
> ... spam = 'cooked'
> ...
> >>> eggs
> [1, 2, 3]
> >>>
>
> The tutorial says this:
>
> If you need to modify the list you are iterating over, e.g., duplicate
> selected items, you must iterate over a copy. The slice notation makes
> this particularly convenient:
>
> >>> for x in a[:]: # make a slice copy of the entire list
> ... if len(x) > 6: a.insert(0, x)
> ...
> >>> a
> ['defenestrate', 'cat', 'window', 'defenestrate']
>
> Understood, but what if you want to modify each element in a list?
> What's the best way to do this in terms of speed and elegance? I guess
> I just haven't seen a good example of this yet in Python

If I know I'm not going to be altering the length of the list, I
generally do it like this:

for i in range(len(a)):
if is_cooked(a[i]):
a[i] = "cooked"

If the list might be changing length, then I generally do

i = 0
while i < len(a):
...

and keep track of i by hand.

HTH
Michael

> What I'm unfortunately used to is this, in Perl:
>
> @eggs = (1,2,3);
> foreach $spam (@eggs) {
> $spam = 'cooked';
> }
> print "@eggs";
> >>> cooked cooked cooked
>
> Thanks,
> Alex Rice
newbie idiom question [ In reply to ]
On 21 Jun 99, Alex Rice wrote:

> Something I keep getting tripped up about is that objects being
> iterated in "for" statements cannot be modified.
>
> I keep trying to do this:
>
> >>> eggs = [1,2,3]
> >>> for spam in eggs:
> ... spam = 'cooked'
> ...
> >>> eggs
> [1, 2, 3]
> >>>
>
> The tutorial says this:
>
> If you need to modify the list you are iterating over, e.g., duplicate
> selected items, you must iterate over a copy. The slice notation makes
> this particularly convenient:

> Understood, but what if you want to modify each element in a list?
> What's the best way to do this in terms of speed and elegance? I guess
> I just haven't seen a good example of this yet in Python

> What I'm unfortunately used to is this, in Perl:
>
> @eggs = (1,2,3);
> foreach $spam (@eggs) {
> $spam = 'cooked';
> }
> print "@eggs";
> >>> cooked cooked cooked

An unwelcome side effect, methinx. You can use range:

for i in range(len(eggs)):
eggs[i] = 'cooked'

Since the list you're iterating over is the same as the list you're
modifying, it may be wise to use a slice (not in this particular
case, but in general):

for i in range(len(mylist[:])):
if mylist[i] == 'ham':
mylist[i:i] = ['eggs'] # insert something

You can also use map:

eggs = map(lambda s: 'cooked', eggs)

But this can get difficult for complex lambdas, and it doesn't change
the list in place.


Hans Nowak (ivnowa@hvision.nl)
Homepage: http://fly.to/zephyrfalcon
newbie idiom question [ In reply to ]
In article <m3emj4yezq.fsf@monsoon.swcp.com>, alex@mindlube.com says...
>
>Something I keep getting tripped up about is that objects being
>iterated in "for" statements cannot be modified.

It depends on whether the objects are mutable or not.

>I keep trying to do this:
>
> >>> eggs = [1,2,3]
> >>> for spam in eggs:
> ... spam = 'cooked'
> ...
> >>> eggs
> [1, 2, 3]
> >>>

Modifying the list by replacement is relatively easy.

eggs = [1,2,3]
for i in range(len(eggs)): eggs[i] = 'cooked'

>The tutorial says this:
>
> If you need to modify the list you are iterating over, e.g., duplicate
> selected items, you must iterate over a copy. The slice notation makes
> this particularly convenient:
>
> >>> for x in a[:]: # make a slice copy of the entire list
> ... if len(x) > 6: a.insert(0, x)
> ...
> >>> a
> ['defenestrate', 'cat', 'window', 'defenestrate']

This applies to insertions and deletions. Without making a copy, the result
in difficult to predict.

>Understood, but what if you want to modify each element in a list?

Do you actually want to change the elements (leaving the list the same) or
change the list?

Terry J. Reedy
newbie idiom question [ In reply to ]
Alex Rice wrote:

> I keep trying to do this:
>
> >>> eggs = [1,2,3]
> >>> for spam in eggs:
> ... spam = 'cooked'
> ...
> >>> eggs
> [1, 2, 3]
> >>>

How about:

>>> eggs = [1,2,3]
>>> for i in range (len (eggs)):
... eggs[i] = 'cooked'
...
>>> eggs
['cooked', 'cooked', 'cooked']
>>>

?

//Klaus

--
···[ Magnetic Ink ]·························································
···[ http://www.magnetic-ink.dk/ ]··········································
newbie idiom question [ In reply to ]
In article <m3emj43fnl.fsf@atrus.jesus.cam.ac.uk>,
Michael Hudson <mwh21@cam.ac.uk> wrote:
>
>If I know I'm not going to be altering the length of the list, I
>generally do it like this:
>
>for i in range(len(a)):
> if is_cooked(a[i]):
> a[i] = "cooked"

And, just as a reminder for anyone reading this, this does not work for
tuples (as I got forcibly reminded yesterday).
--
--- Aahz (@netcom.com)

Hugs and backrubs -- I break Rule 6 <*> http://www.rahul.net/aahz/
Androgynous poly kinky vanilla queer het

Disciple of Uncle Chuck
newbie idiom question [ In reply to ]
To add to the confusion, consider

>>> eggs = [[1], [2], [3]]
>>> for spam in eggs:
... spam[:]=['cooked'] # or spam[0] = 'cooked'
...
... eggs
[['cooked'], ['cooked'], ['cooked']]

Here, the item "spam" is a mutable object, and I modify it.

In your case,
> >>> for spam in eggs:
> ... spam = 'cooked'

spam begins the loop as a reference to something from eggs, but all you do
is make spam a reference to something else. (which doesn't change the
references in eggs)

Remember, in Python the "=" operator means "put a reference to the RHS in
the LHS", and has no effect on the object that the LHS used to be a
reference to (if it had a prior value). Well, except in the case of
setitem/setslice, of course. (which is what I used in the above example)

Is this a wart on python, that "=" means three different things depending
if you have a name, a [item] or a [slice:] on the LHS?

Jeff
newbie idiom question [ In reply to ]
Jeff Epler wrote:
>
> Is this a wart on python, that "=" means three different things depending
> if you have a name, a [item] or a [slice:] on the LHS?

Syntactically, '=' doesn't stand on its own - you
have to read it in conjunction with the LHS.

In other words, there isn't an '=' operator, there
are 'x=', 'x.y=', 'x[y]=' and 'x[y:z]=' operators.

It's not a wart, it's a feature...

Greg
newbie idiom question [ In reply to ]
Thanks everyone. I think I grok it now.

Alex Rice

Michael Hudson <mwh21@cam.ac.uk> writes:

>
> If I know I'm not going to be altering the length of the list, I
> generally do it like this:
>
> for i in range(len(a)):
> if is_cooked(a[i]):
> a[i] = "cooked"
>
> If the list might be changing length, then I generally do
>
> i = 0
> while i < len(a):
> ...
>
> and keep track of i by hand.
>
> HTH
> Michael