Strange behavior of locals() function in nested functions - python

To serve as closure function, it is important for an inner functions to have access to variables defined in surrounding scopes.
But why does locals() function tell us that these variables are member of local namespace, even if they are referenced to after invokation of locals()?
I referenced to a nonlocal variable x after invocation of locals(), even after return statement. I even preceded it with keyword nonlocal.
But nothing changed.
def f():
x=0
def g():
print(locals())
return
nonlocal x
g() # prints "{'x': 0}"
x=1
g() # prints "{'x': 1}
return g
g=f()
g() # prints "{'x': 1}
Since x is referenced after (and not before) invocation of locals(), I would expect that an empty dictionary {} is printed.
But instead, the result is first {'x': 0}, and after changing within outer scope {'x': 1}.
Actually, it isn't even a local variable, since it can be modified from surrounding scope.
What is actually going on?

Local symbol table stores all information related to the local scope of the program, and is accessed in Python using locals() method. however more details can be accessed from scope

Related

Consider Three Nested Functions. Can the Innermost Function Access the Namespace of the Outermost One?

I tested this out with a program I wrote myself:
>>> def f():
f=['f',1,2]
def g():
g=1
print('this prints out f from f(): ',f)
print("id",id(f))
def x():
x=1
print('this also prints out f from f():',f)
print('id',id(f))
x()
g()
>>> f()#output
this prints out f from f(): ['f', 1, 2]
id 140601546763464
this also prints out f from f(): ['f', 1, 2]
id 140601546763464
From what I learned, the innermost x() function can only access its own local namespace, the enclosing namespace, the global, and finally the built-in namespace. I initially thought that trying to access the list f declared in function f() from function x() would raise an error, as the f() function's namespace cannot be classified as any of the aforementioned elements. After running the program, I realized you indeed can access the list f from the function x(). I don't quite understand how this works though. My guess is that checking the enclosing namespace not only checks the local namespace of the enclosing function but the enclosing function for it as well, in a process that works almost recursively. Can somebody please explain how this works?
Python resolves names using LEGB rule:(LEGB means Local, Enclosing, Global, and Built-in)
Local scope:
contains the names that are defined inside the function.
visible only inside the function
created at function call(If we call the function multiple times each call creates new local scope)
will be destroyed once function return
Enclosing or nonlocal:
exists for nested functions
contains names defined in the enclosing function
visible in inner and enclosing functions.
Global:
contains all the names defined at the top level of a program
visible from everywhere inside the code.
exist throut the life of code.
Built-in:
created whenever we run a script
contains keywords, functions, exceptions, etc that are built into Python
visible everywhere in the code
The LEGB rule is a rule which determines the order in which Python looks up names.
i.e Python will look the name sequentially in the local, enclosing, global, and built-in scope. And inner scope codes can outer scope names but outer scope codes cannot access inner scope names.
When we use nested functions the scope resolving is as follows:
check the local scope(inside the function)
If not found check enclosing scopes of outer functions from the innermost scope to the outermost scope
If not found look the global scope
If not found look built-ins
Still not found raise error

How pass a value to a variable in python function with using exec()?

A little easy problem:
exec("a=3")
print(a)
# This will print 3
If I use this:
def func():
exec("a=3")
print(a)
func()
# NameError: name 'a' is not defined.
What happened?How could I use exec() to assign it a value in a function?
Edit:I found a question with the same trouble but still didn't solved.
why do you want to do that?
I know using exec() is bad and unsafe.But recently I try to solve a OP's problem.I met it.
Python knows several kinds of scope: module global, function local, nonlocal closures, class body. Notably, scope resolution is defined statically at byte code compile time – most importantly, whether names refer to local/nonlocal or global scope cannot be changed.
Of these scopes, only global scope is guaranteed to behave similar to a dict, and as such writeable. The local/nonlocal scope is generally not writeable, and new variables cannot be added to it.
exec will write to the global scope if locals is not passed in; globals must then explicitly be set to its default of globals().
def func():
exec("a='exec'", globals()) # access only global scope
print(a)
a = 'global'
func() # prints exec
However, once a name is local to a function, exec cannot modify it.
def func():
a = 'local' # assignment makes name local
exec("a='exec global'", globals())
exec("a='exec locals'", globals(), locals())
print(a)
a = 'global'
func() # prints local
While a dict-like representation of local/nonlocal scope exists, the interpreter is not required to honour changes to it.
locals()
Update and return a dictionary representing the current local symbol table. Free variables are returned by locals() when it is called in function blocks, but not in class blocks. Note that at the module level, locals() and globals() are the same dictionary.
Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
Even though exec does take locals as a dict, these are not treated like function locals/nonlocals. Attempts to modify the default locals (the result of locals()) are not defined.
exec()
... If globals and locals are given, they are used for the global and local variables, respectively. If provided, locals can be any mapping object. Remember that at module level, globals and locals are the same dictionary. If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.
Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. ...
execute help(exec) in your python3 REPL, you will get the following docuement:
Help on built-in function exec in module builtins:
exec(source, globals=None, locals=None, /)
Execute the given source in the context of globals and locals.
The source may be a string representing one or more Python statements
or a code object as returned by compile().
The globals must be a dictionary and locals can be any mapping,
defaulting to the current globals and locals.
If only globals is given, locals defaults to it.
so there are at least 2 options to provide the value of argument 'a':
assign a value to the variable 'a' in the module's global scope:
a = 1
def func():
exec("global a;a=3")
print(a)
pass a customized global or local context to exec:
def func():
my_context = {'a': 1}
exec("a=3", None, my_context)
print(my_context['a'])
NOTE: DONT USE eval or exec IN YOUR SERIOUS CODE UNLESS YOU KNOW WHAT YOU ARE DOING.
EDIT NOTE
the following solution(2th solution mentioned in the comments) wont work:
def func():
a = 1
exec("a=3")
print(a) # still get 1 here

Python non-local variables

Isn't it supposed to return 'inner: nonlocal' and 'outer: local'?? Can someone explain what's happening, thanks.
>>>>def outer():
x = 'local'
def inner():
nonlocal x
x='nonlocal'
print('inner:',x)
inner()
print('outer:',x)
>>>outer()
inner: nonlocal
outer: nonlocal
You are setting x to 'nonlocal' by calling inner(). Thus, x is set to 'nonlocal' no matter where you try to print it from within outer(). Put the print statement before the call to inner() and it will work.
See enter link description here link for information on non-local.
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.
In short, it lets you assign values to a variable in an outer (but non-global) scope.
If you remove the keyword nonlocal and try your program, you will observe:
inner: nonlocal
outer: local
Program:
def outer():
x = 'local'
def inner():
x='nonlocal'
print('inner:', x)
inner()
print('outer:', x)
outer()

How to get all variables available in scope?

If I have the following code:
var_global = "global"
def outer() :
var_outer = "outer"
def inner() :
var_inner = "inner"
# How to get here all variables available (it should contain
# var_inner, var_outer, var_global)?
print("inner", locals(), globals())
return inner
And I run:
outer()()
I would like to get all variables at print location. This should include var_outer as well. But neither locals nor globals provide it.
The closure variable mechanisms that would provide inner with access to var_outer are only triggered if inner actually contains accesses to the var_outer variable. Otherwise, var_outer is not saved, and no introspection will give you access to it.
var_outer truly ISN'T available in inner(). Making a variable available in a nested function requires that the variable be stored in a different way from the start; Python did not do so, due to lack of any reference to the variable from a nested function. If you did insert such a reference (such as the bare statement var_outer), it would show up in the output of locals().
var_outer can access var_inner by nonlocal.
var_global = "global"
def outer():
var_outer = "outer"
def inner():
var_inner = "inner"
print("inner", locals(), globals())
nonlocal var_outer
print(var_outer)
return inner
outer()()
Will print:
inner {'var_outer': 'outer', 'var_inner': 'inner'} [...]

When is the locals dictionary set?

A module holds a dictionary to keep track of itscontext, such as the names defined at some point of the execution. This dictionary can be accessed through vars(module) (or module.__dict__) if module was imported, or by a call to the locals built-in function in the module itself:
Update and return a dictionary representing the current local symbol table.
But I found myself a bit confused, when I tried accessing the locals dictionary from a function. The output of a script containing only the following is an empty dictionary:
def list_locals():
print(locals())
list_locals()
But on the other hand, if a script contains exclusively the following, the output is the expected dictionary, containing __name__, __doc__ and the other module-level variables:
print(locals())
So, when is the content of the locals dictionary set?
In addition, what does "update" mean in the definition of the locals function?
The namespace of a module is the global namespace, accessed through globals(). There is no separate locals namespace, so locals(), outside of functions, just returns the global namespace.
Only functions have a local namespace. Note that locals() is a one-way reflection of that namespace; the CPython implementation for functions is highly optimised and you can't add or alter local variables through the locals() dictionary. The dictionary returned by locals() is updated whenever the namespace has changed between calls to that function.
Note also that things like list / dict / set comprehensions, generator expressions and class bodies are in reality executed as functions too, albeit with different namespace rules. In such contexts locals() will also return the separate function local namespace.
If you call locals() more than once during the same function call, it will return the same dictionary.
>>> def f():
... d = locals()
... assert 'd' not in d
... locals()
... assert 'd' in d
... assert d is locals()
... print(d)
...
>>> f()
{'d': {...}}
"Update" in this case means that the contents of this dictionary are updated to reflect the current scope of local variables that exist, but if you kept a reference to this dictionary then that dictionary will still be used.
Notice also that the locals dictionary doesn't contain a key for 'f', even though you could have accessed it during the course of the function. In case it's not obvious, that's a global, not a local.

Categories

Resources