How are python variable names resolved? (explanations on the web are a bit uncertain)

47 views Asked by At

It seems that there are two things that are not being distinguished in some guides about this.

When a function is running, there is its local scope, and some scopes above her on call stack. Each scope has its local variables.

When a function is defined, there is also a call stack with scopes.

So when we reference a variable in the function, its name is resolved via rule LEGB. Does that rule apply to call stack of when it was defined, or the current call stack when it is running? I think it's the first one:

def f():
    y = 'when_defined'
    
    def g():
        print(y)
    
    return g
 
def h(g):
    y = 'when_running'
    g()
 
g = f()
h(g)
>>> when_defined

But in case when it comes to global variables, they don't have to be defined when the function is being defined, and can still be looked up

def h():
    print(x)
 
x = 1
h()
>>> 1

So my understanding is:

  1. When the function is defined, it decides for itself which of used in it variables are local (namely if they are write-encountered in the code, or are in args), which of the other ones are from enclosed scopes (and save an object for those names in fun.__closure__). Other variables don't get a classification (but can then only be looked up in global or builtins). Operators nonlocal, global force the type of variable they are used on to be "first possible from EGB", and "global", respectvely.

  2. When the function is running, and resolving a read on variable name, it first looks if it is a "local"-classified variable or not. If it is, it gives it from locals() (and fails if it wasn't assigned yet). Otherwise if it is "enclosed", gives the value from fun.__closure__. Then, it looks for it in globals(). And if its not there, in builtins().

Is this understanding correct? Could you give me a place where I can look for the exact behaviour of this?

Is it also true that when doing an import, we are running a new module, so it can't be referencing anything above in the call stack, because its definition basically has no upper context? And we can simply say that it runs on its own, and then import adds its locals() to our locals()?

0

There are 0 answers