joueffy
joueffy

Reputation: 41

For-loop statement evaluation

It's agreed upon that the iterable item in a for loop is evaluated only once. Hence, the output f called is printed once.

What I'm struggling to figure out is, why am I not getting an out-of-bound error? Since at the time of the evaluation, the length was equal to 100.

mylist = [x for x in range(100)]

def printmylist(thelist):
    print("f called")
    return thelist

for i, item in enumerate(printmylist(mylist)):
    print(len(mylist))
    del mylist[len(mylist) - i - 1]

f called 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51

Upvotes: 4

Views: 202

Answers (2)

Ian
Ian

Reputation: 30813

Though the print("f called") is only called once, the variable (called iterable object which stores the evaluation value of enumerate(printmylist(mylist)) in your case has its referred set change from time to time because you remove the enumeration one by one. At first you have all the 100 items, but it is reduced one by one:

Note: len(mylist) - i - 1 = 99, delete index 99 -> 100 removed
1 2 3 ... 98 99 100 #len(mylist) = 100 
^ <-- here is i = 0

Note: len(mylist) - i - 1 = 98, delete index 98 -> 99 removed
1 2 3 ... 98 99 #len(mylist) = 99
  ^ <-- here is i = 1

Note: len(mylist) - i - 1 = 97, delete index 97 -> 98 removed
1 2 3 ... 98 #len(mylist) = 98
    ^ <-- here is i = 2

... and so on

Plus, since i will be checked every end of the loop against that variable (the iterable object, therefore, you only print 50 items out of 100 items.

From the Python for statement documentation:

The expression list is evaluated once; it should yield an iterable object. An iterator is created for the result of the expression_list. The suite is then executed once for each item provided by the iterator, in the order of ascending indices. Each item in turn is assigned to the target list using the standard rules for assignments, and then the suite is executed. When the items are exhausted (which is immediately when the sequence is empty), the suite in the else clause, if present, is executed, and the loop terminates.

To simplify, imagine you have

v = enumerate(printmylist(mylist)) #v is the iterable object

Though it is only evaluated once, the set where v is pointed to changes every time you remove an item but i iterates over v. Thus, i is going to be increasing increasing till finally i = 50.

.
.
.

v = ... 50, i = 49
        ^ <-- here is i and is in v

v = ... 50    , i = 50 and is out from v
           ^ <-- no more item found

No more item is left to be evaluated beyond the border. Thus you get the results:

100 99 ... 51

And not getting out of bounds.

More on how for works internally, including how it creates internal indexer and checks it against the value pointed by the termination value every time.

Upvotes: 2

Matt Jordan
Matt Jordan

Reputation: 2181

In the for loop, you use the current length of the list to evaluate the location to delete. And, you delete from the end of the list. So your loop only iterates 50 times.

If you change the print statement to display i, you will find it only counts from 0 to 49.

Upvotes: 2

Related Questions