No. The nonlocal
keyword is ONLY needed when you assign to a name. It is not required when you only refer to it. This is the same as with the global
statement. It would confuse people GREATLY if suddenly there’s one place where that rule is violated.
Sure. Have a quick look back at my example with eval for context.
A function’s local variables are, by default, only valid while that function is executing. (I’m going to assume it’s not an async or generator function here; the rules are a bit more complicated for those, but the concepts are the same.) The function begins, then it ends, and every variable it had is gone. Any objects that were referenced in those variables and not anywhere else can now be disposed of. This is true even if the function has an inner function:
def outer(x):
def inner():
print("I am the inner function!")
return inner
Yes, the inner function might still exist, but it doesn’t refer to x, so that variable can be disposed of. If that’s a gigantic thing that takes up a crazy amount of memory, that can make a pretty big difference!
But what if the inner function DOES refer to that variable? Well, now every use of that name, whether in the outer or the inner function, has to be a slower “closure-friendly” lookup.
def outer1(x):
def inner():
print("Hello, world!")
x += 2
return inner, x
def outer2(x):
def inner():
print("Hello, world!", x)
x += 2
return inner, x
$ python3 -m timeit -s 'from deref import outer1' 'outer1(5)'
5000000 loops, best of 5: 47.3 nsec per loop
$ python3 -m timeit -s 'from deref import outer2' 'outer2(5)'
5000000 loops, best of 5: 65.8 nsec per loop
This isn’t TOO bad - closure references are also quite highly optimized - but it’s a price you don’t want to pay without needing to. And that’s how it is in CPython - you only pay this when there is actually a closure that makes reference to a particular name. If there were multiple variables and only some of them were referenced by the inner function, only those variables would become closure cells, so you don’t pay the price for the others.
Enter deferred expressions. Suddenly, ANYTHING could be a deferred. That print
function? Well, maybe there’s a deferred expression injected into the module globals. We can’t know. With the semantics that I was responding to at the time (by the way, one of the REALLY annoying things about debating these kinds of half-baked proposals is that the semantics are like jelly nailed to a tree, in the hot sun, with a swarm of ants going for it - by the time you look at it, it’s already changed or vanished), the “undeferring” of that thing would potentially reference names in that scope. That means, just in case, Python has to store all variables in closure cells.
And that’s a price we should not have to pay, both in performance and in nightmarish action-at-a-distance.