GeoGeoGeometry GeoGeoGeometry - 1 year ago 82
Python Question

Python list insertion when iterating

I'm working with python 3.5, and insert isn't behaving as I expect it to.

scorelist = ["x","x"]

for q in range(0, len(scorelist)):
if scorelist[q] == "x":
scorelist.insert((q), " ")

I'd expect that to leave the least reading as follows

scorelist = ["x", " ", "x", " "]

Instead, it leaves me with

scorelist = [" ", " ", "x", "x"]

I'm pretty stumped as to why this is the output. Any guidance would be greatly appreciated.

Answer Source

It's generally a Bad Idea to mutate any container while iterating over it.

In your case, len(scorelist) is evaluated once, at the time the for loop begins. The length is 2 at that time, so the range is just [0, 1]. Nothing later can possibly change that.

In the first iteration, q is 0, and scorelist[0] is "x", so

scorelist.insert(0, " ")

leaves scorelist as

[" ", "x", "x"]

In the second iteration, q is 1, and for the new value of scorelist, scorelist[1] is still "x", so

scorelist.insert(1, " ")

leaves scorelist as

[" ", " ", "x", "x"]

Then the loop ends. There's nothing really mysterious about it, but it's far from obvious either - which is why it's a Bad Idea to mutate containers while iterating over them ;-)


If you're determined to insert a blank at the end, and between every pair of elements before that, here's one obscure way to do it:

for q in reversed(range(1, len(scorelist) + 1)):
    scorelist.insert(q, " ")

Then, e.g., if scorelist starts as

['x', 'y', 'z']

that loop will leave it as

['x', ' ', 'y', ' ', 'z', ' ']

By iterating "backwards", the insertions don't interfere with the original indices of the original list elements. If that's confusing (hint: it is! ;-) ), just work it out one iteration at a time, as I spelled it out for your original code.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download