-2

Here in this code, the fourth line(print(i)) is a little bit confusing. When I print it, I am getting the value of 10. Can someone explain what actually happened?

def ranges2():
    for i in [1, 3, 5, 7, 9, 10]:
        print(i, ":", i ** 3)
    print(i)

ranges2()

#result is 10................ why?
ranges2()
Prune
  • 76,765
  • 14
  • 60
  • 81
Olix
  • 3
  • 2
  • `for i in ...` what is `i` at each iteration? What is `i` in the final iteration? – G. Anderson Jun 03 '21 at 20:42
  • There’s no global variable involved. – DisappointedByUnaccountableMod Jun 03 '21 at 20:45
  • @12944qwerty: Doesn't really matter, but they tagged the question for 2.7 (though they use 3.x-like `print`s, which could just mean they omitted the `__future__` import, or they are printing `tuple`s). Doesn't change the answer either way; both versions had the same scope rules for this case. – ShadowRanger Jun 03 '21 at 20:48
  • @ShadowRanger Yeah, I realize that. I just commented too quickly... oops – 12944qwerty Jun 03 '21 at 20:48
  • @barny exactly, no global variable but the output I got was 10. I also did some little adjustments by adding more values to the array. I noticed that the output is always the last index of the array. e.g. def ranges2(): for i in [1, 3, 5, 7, 9, 10, 11, 12]: print(i, ":", i ** 3) print(i) The result will be 12. What I expected is to give an error but it wasn't. Just wanted to know what exactly is going on – Olix Jun 03 '21 at 21:00
  • A local variable in a function (or method) is in scope anywhere in the function. – DisappointedByUnaccountableMod Jun 03 '21 at 21:04

2 Answers2

2

Python doesn't have a concept similar to block scoping that would narrow the lifetime of i to the for loop that defines it; all local variables are scoped to the entire function, and persist until the function is complete (del can unbind the name from the value, but if you reassign it, it's assigned to the same storage location)1.

After the for loop completes, i just has whatever was iterated last (the last thing in the sequence in this case, though use of break within the loop could lead to it being an earlier value), and keeps it until you reassign it or the function completes (by exception or return, implicit or explicit).

1 Technically, CPython 3.x does automatically unbind caught exceptions after the exception handling completes, to avoid cyclic references delaying object cleanup. This behaves a little like block scoping, but it's equivalent to the explicit del case; the name continues to exist, it's just not bound to any object.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thank you so much 1. What did I learn? 2. All local variables are scoped to the entire function in python After the for loop completes, i is whatever was iterated last (the last thing in the sequence) Can I get a sample of how the del is being used in a code? – Olix Jun 03 '21 at 21:07
  • @NoibiOlamilekan: You could insert a `del i` as a statement anywhere in your existing code, and after the `del`, until `i` is assigned a new value, attempts to access `i` would raise an `UnboundLocalError`. There's rarely any need for `del` (in this usage; it's useful for deleting specific keys in a `dict` or deleting slices of a `list` on occasion), since once you exit the function and its call frame is clear (usually immediate, sometimes delayed if an exception bubbled out of the function, cleared when the exception is fully handled) all the locals are effectively `del`-ed anyway. – ShadowRanger Jun 04 '21 at 17:38
  • @NoibiOlamilekan: If you want more on `del`, see [When is del useful in Python?](https://stackoverflow.com/q/6146963/364696) with some simple examples (one of the answers demonstrates using an explicit `del` to immediately unbind the loop variable after a loop; usually pointless, but if the loop variable might be bound to something expensive to hold on to, it might let you reclaim memory faster). – ShadowRanger Jun 04 '21 at 17:43
0

What's happening is that there's an implicit 'i' created by your for loop, and afterward it still exists and contains the last value it was set to by the for loop.

pjz
  • 41,842
  • 6
  • 48
  • 60