Here's another one, and the way I think about it:
def _loop(i):
functions.append(lambda: print(f"Hello {i}"))
_loop(0)
_loop(1)
_loop(2)
print(i)
# Error!
I highly prefer lexical scoping, where the variables are bound to the block they were declared in, like Javascript's `let` vs the old `var`. This avoids shadowing and general namespace pollution.I know it's not how Python operates, but I think it's how it should. Though I'd argue the syntactical similarities between functions and loops nudge users towards this second model.