Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is the existence of nonlocal variables checked?

I am learning Python and right now I am on the topic of scopes and nonlocal statement. At some point I thought I figured it all out, but then nonlocal came and broke everything down.

Example number 1:

print( "let's begin" )
def a():
    def b():
        nonlocal x
        x = 20
    b()

a()

Running it naturally fails.
What is more interesting is that print() does not get executed. Why?.

My understanding was that enclosing def a() is not executed until print() is executed, and nested def b() is executed only when a() is called. I am confused...

Ok, let's try example number 2:

print( "let's begin" )
def a():
    if False: x = 10
    def b():
        nonlocal x
        x = 20
    b()

a()

Aaand... it runs fine. Whaaat?! How did THAT fix it? x = 10 in function a is never executed!

My understanding was that nonlocal statement is evaluated and executed at run-time, searching enclosing function's call contexts and binding local name x to some particular "outer" x. And if there is no x in outer functions - raise an exception. Again, at run-time.

But now it looks like this is done at the time of syntax analysis, with pretty dumb check "look in outer functions for x = blah, if there is something like this - we're fine," even if that x = blah is never executed...

Can anybody explain me when and how nonlocal statement is processed?

like image 873
Vasyl Demianov Avatar asked Dec 14 '17 13:12

Vasyl Demianov


People also ask

What are nonlocal variables in Python?

In python, nonlocal variables refer to all those variables that are declared within nested functions. The local scope of a nonlocal variable is not defined. This essentially means that the variable exists neither in the local scope nor in the global scope.

What is the difference between global and nonlocal?

An important difference between nonlocal and global is that the a nonlocal variable must have been already bound in the enclosing namespace (otherwise an syntaxError will be raised) while a global declaration in a local scope does not require the variable is pre-bound (it will create a new binding in the global ...

How long do local variables exist?

They exist for the lifetime of the program, even though their scope is only the function in which they are defined. If a local, static variable is initialized in its declaration within a function, it is initalized only once.

What is nonlocal and global in Python?

A nonlocal variable has to be defined in the enclosing function scope. If the variable is not defined in the enclosing function scope, the variable cannot be defined in the nested scope. This is another difference to the "global" semantics.


1 Answers

You can see what the scope of b knows about free variables (available for binding) from the scope of a, like so:

import inspect

print( "let's begin" )

def a():
    if False:
        x = 10

    def b():
        print(inspect.currentframe().f_code.co_freevars)
        nonlocal x
        x = 20

    b()

a()

Which gives:

let's begin
('x',)

If you comment out the nonlocal line, and remove the if statement with x inside, the you'll see the free variables available to b is just ().

So let's look at what bytecode instruction this generates, by putting the definition of a into IPython and then using dis.dis:

In [3]: import dis

In [4]: dis.dis(a)
  5           0 LOAD_CLOSURE             0 (x)
              2 BUILD_TUPLE              1
              4 LOAD_CONST               1 (<code object b at 0x7efceaa256f0, file "<ipython-input-1-20ba94fb8214>", line 5>)
              6 LOAD_CONST               2 ('a.<locals>.b')
              8 MAKE_FUNCTION            8
             10 STORE_FAST               0 (b)

 10          12 LOAD_FAST                0 (b)
             14 CALL_FUNCTION            0
             16 POP_TOP
             18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

So then let's look at how LOAD_CLOSURE is processed in ceval.c.

TARGET(LOAD_CLOSURE) {
    PyObject *cell = freevars[oparg];
    Py_INCREF(cell);
    PUSH(cell);
    DISPATCH();
}

So we see it must look up x from freevars of the enclosing scope(s).

This is mentioned in the Execution Model documentation, where it says:

The nonlocal statement causes corresponding names to refer to previously bound variables in the nearest enclosing function scope. SyntaxError is raised at compile time if the given name does not exist in any enclosing function scope.

like image 195
ely Avatar answered Sep 17 '22 00:09

ely