3

[First-ever question from first-ever Stack-o-flow Python beginner user]

I have been trying to create a function that appends numbers 1 to 10 and then erase from 10 to 1 in python list object. I succeeded however, I bumped into a weird behavior of for-in loops.

Below is what I did & succeeded:

def a():
    li = []

    for i in range(1,11):
        li.append(i)
        print(li)

    for n in range(1,11):
        li.remove(li[-1])
        print(li)

a()

would print:

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 2, 3]
[1, 2]
[1]
[]

However, if I change the second part of its for-loop range(1,11) to li, the iteration stops as shown below.

def a():
    li = []

    for i in range(1,11):
        li.append(i)
        print(li)

    for n in li:         <-------------- THIS PART
        li.remove(li[-1])
        print(li)

a()

Would print:

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5]

So, I checked its length of the object li.

def a():
    li = []
    for i in range(1,11):
        li.append(i)
        print(li)

    print("Length before passed: ", len(li))

    for n in li: <------------ THIS
        print("Length BEFORE REMOVE: ", len(li))

        li.remove(li[-1])

        print("Length AFTER REMOVE: ", len(li))
        print(li)
a()

Would print:

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Length before passed:  10
Length BEFORE REMOVE:  10
Length AFTER REMOVE:  9
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Length BEFORE REMOVE:  9
Length AFTER REMOVE:  8
[1, 2, 3, 4, 5, 6, 7, 8]
Length BEFORE REMOVE:  8
Length AFTER REMOVE:  7
[1, 2, 3, 4, 5, 6, 7]
Length BEFORE REMOVE:  7
Length AFTER REMOVE:  6
[1, 2, 3, 4, 5, 6]
Length BEFORE REMOVE:  6
Length AFTER REMOVE:  5
[1, 2, 3, 4, 5] <---- And it stops here.

What is going on here? Why would python for-loop stop before its full cycle?

Thank you very much.

4 Answers4

6

In your case, the iteration stops because you're iterating from the start, but you're removing elements from the end at the same time:

  • The 1st iteration ends with 9 elements, so the loop proceeds to the 2nd element.
  • The 2nd iteration ends with 8 elements, so the loop proceeds to the 3rd element
  • ...
  • The 5th iteration ends with 5 elements, and there's no 6th element to proceed to and the loop ends.

This is why mutating a list while iterating it is generally inadvisable.

avramov
  • 2,119
  • 2
  • 18
  • 41
4

Try running this, you'll understand immediately

def a():
    li = []

    for i in range(1,11):
        li.append(i)
        print(li)

    for n in li:
        print(n)
        li.remove(li[-1])
        print(li)

a()

By removing elements this happens:

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1
[1, 2, 3, 4, 5, 6, 7, 8, 9]
2
[1, 2, 3, 4, 5, 6, 7, 8]
3
[1, 2, 3, 4, 5, 6, 7]
4
[1, 2, 3, 4, 5, 6]
5
[1, 2, 3, 4, 5]
Dev_Man
  • 847
  • 1
  • 10
  • 28
0

You add then print 10 times, you remove then print 10 times - simplified:

a = []
a.append(1)
print(a)         # [1]
a.pop()
print(a)         # []

You start with an empty list and print after adding - you stop with an empty list and print.

The problem with the second code part is: you are modifying an iterable while iterating - that is generally a bad idea.


If you change:

def a():
    li = []

    for i in range(1,11):
        li.append(i)
        print(li)

    while(li):
        li.pop()  # same but easier - removes last element without params
        if(li):   # test if non empty
            print(li)

or optimized:

    for _ in  range(len(li)-1): # one less so it stops bevore removing the last elem
        li.pop()
        print(li)

you get the desired output.

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
0

It's cause you are iterating the list from the start to the end and you are always removing the last element. As remove method alter the list and the for-in loop from python consider this alterations, you iterate over the five first itens and remove the five last ones.

My sugestion is do something like this:

def a():
  li = []

  for i in range(1,11):
    li.append(i)
    print(li)

  print("Length before passed: ", len(li))

  # Remove the last item while has items
  while len(li):
    li.pop()