Saturday, 11 August 2007

Iterating over lists in Python

Note to self: It's a bad idea to change a list in the middle of a loop iterating over it. The following doesn't work:
for item in list:
    if foo:
        list.remove(list)
And must be replaced with:
for item in list[:]:
    if foo:
        list.remove(list)
On the wrong way, the loop doesn't execute on items positioned immediately after the removed ones. This has cost me a big amount of debugging time...

4 comments:

Ethan said...

I was bit by this very thing a few days ago. I think it's stupid. Either:

1) make it work correctly
2) raise an error, like on dicts, when the object changes while iterating.

Ethan

Steve Holt! said...

I was a TA for a first-year University course in Python, and this was one of the trickiest parts of the last assignment for the students. The mutable variables idea becomes interesting and useful though, once you get used to it. I consider it a feature.

slashdotaccount said...

Sounds like a bug, did you file one?

Sam Morris said...

This can be written more clearly in a couple of other ways:

filter (condition_test (item), l)

filter returns a list that consists of all items in l where condition_test (item) returned True. If condition_test consists of a single expression then you can use a lambda instead of creating & calling a separate function:

filter (lambda i: i > 3, l)

Python 2.3 added List Comprehension syntax, which looks like this:

[i for i in l if condition_test (i)]

If condition_test is a single expression, it can be used directly:

[i for i in l if i > 3]