Read-only Namespace in Python - python

What does read-value mean in the context of names?
In [1]: def outer():
...: x=1
...: def inner():
...: print x
...: inner()
...:
In [2]: outer()
1
Like in the above example x in not the namespace of inner(). Do variables in namespaces have types such as read-only/ writeable etc?
Quoting official docs: "To rebind variables found outside of the innermost
scope, the nonlocal statement can be used; if not declared nonlocal,
those variable are read-only (an attempt to write to such a variable
will simply create a new local variable in the innermost scope,
leaving the identically named outer variable unchanged)."
Reference: https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces (5 th last paragraph)

No, variables don't have 'properties' like read-only or write. The namespace implementation may disallow direct manipulation, but that's something entirely different.
The nonlocal and global statements let you override the scope of a variable; without these Python will make a name local if it is being bound to. Binding actions include assignment, for loop, with .. as and except .. as statement targets, function arguments and imports. Changing the scope doesn't alter if a variable is read-only or not, it merely changes the scope that manages the variable. nonlocal means it'll be stored in a parent scope and made a closure.
The term read-only used by the quote you found is very misleading; assignment clearly still alters the local variable. The presence of local doesn't make the same name in the parent scope not writable, it is simply not visible in the innermost scope. It's disappointing that this is from the official tutorials; I've reported a bug to see this corrected.

Related

Behaviour discrepancy between classes and functions scope in Python

As you may know, the scope of a variable is statically determined in python (What are the rules for local and global variables in Python?).
For instance :
a = "global"
def function():
print(a)
a = "local"
function()
# UnboundLocalError: local variable 'a' referenced before assignment
The same rule applies to classes, but it seems to default to the global scope instead of raising an AttributeError:
a = "global"
class C():
print(a)
a = "local"
# 'global'
Moreover, in the case of a nested function, the behavior is the same (without using nonlocal or global) :
a = "global"
def outer_func():
a = "outer"
def inner_func():
print(a)
a = "local"
inner_func()
outer_func()
# UnboundLocalError: local variable 'a' referenced before assignment
But in the case of nested classes, it still defaults to the global scope, and not the outer scope (again without using global or nonlocal) :
a = "global"
def outer_func():
a = "outer"
class InnerClass:
print(a)
a = "local"
outer_func()
# 'global'
The weirdest part is that the nested class default to the outer scope when there is no declaration of a :
a = "global"
def outer_func():
a = "outer"
class InnerClass:
print(a)
outer_func()
# 'outer'
So my questions are :
Why the discrepancy between functions and classes (one raising an exception, the other defaulting to the global scope.
In nested classes, why the default scope has to become global instead of keeping using the outer one when using a variable defined afterward?
The answer is given in great detail in Section 9.2 of the official docs. The crux of the matter is
... On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)
When you are in the class definition, which at the moment of its execution is the innermost scope, dynamic name resolution applies. You therefore see printouts of the global value of a.
If the name resolution were static, as in function definitions, the name a would be recognized as a local name even in the print statement. That is why you can't print a in a function before assigning to it.
The rules for class body scoping are alluded to in Section 4.2.2:
Class definition blocks and arguments to exec() and eval() are special in the context of name resolution. A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.
Let's parse that last sentence carefully, because it fully covers your last two examples. First off, what is an unbound local variable in this context? A class body creates a new namespace, just like entering a function. If a name is bound somewhere in a class body, it is a local variable. This is determined statically, as mentioned above. If you attempt to reference the name before it is first bound, you have an unbound local variable. Instead of raising an error, as a function call would do, python jumps straight to the global namespace to perform the lookup (and ignores builtins as well). In all other cases (not local variables), normal LEGB lookup order applies.
This is indeed a bit counter-intuitive, and I would argue that it pushes if not outright breaks the rule of least surprise.

Why does writing to a variable change its scope?

Take the following code sample
var = True
def func1():
if var:
print("True")
else:
print("False")
# var = True
func1()
This prints True as one would expect.
However, if I uncomment # var = True, I get the error
UnboundLocalError: local variable 'var' referenced before assignment
Why does writing to a variable make an otherwise accessible variable inaccessible? What was the rationale behind this design choice?
Note I know how to solve it (with the global keyword). My question is why was it decided to behave this way.
Because:
Namespaces exist: the same variable name can be used at module level and inside functions, and have nothing to do with each other.
Python does not require variables to be declared, for ease of use
There still needs to be a way to distinguish between local and global variables
In cases where there is likely unexpected behavior, it is better to throw an error than to silently accept it
So Python chose the rule "if a variable name is assigned to within a function, then that name refers to a local variable" (because if it's never assigned to, it clearly isn't local as it never gets a value).
Your code could have been interpreted as using the module-level variable first (in the if: line), and then using the local variable later for the assignment. But, that will very often not be the expected behavior. So Guido decided that Python would not work like that, and throw the error instead.
Python defaults to implicit variable declaration via assignment, in order to remove the need for additional explicit declarations. Just "implicit declaration" leaves several options what assignment in nested scopes means, most prominently:
Assignment always declares a variable in the inner-most scope.
Assignment always declares a variable in the outer-most scope.
Assignment declares a variable in the inner-most scope, unless declared in any outer scope.
Assignment declares a variable in the inner-most scope, readable only after assignment.
The latter two options mean that a variable does not have a scope well-defined just by the assignment itself. They are "declaration via assignment + X" which can lead to unintended interactions between unrelated code.
That leaves the decision of whether "writing to a variable" should preferably happen to isolated local or shared global variables.
The Python designers consider it more important to explicitly mark writing to a global variable.
Python FAQ: Why am I getting an UnboundLocalError when the variable has a value?
[...]
This explicit declaration is required in order to remind you that (...) you are actually modifying the value of the variable in the outer scope
This is an intentional asymmetry towards purely reading globals, which is considered proper practice.
Python FAQ: What are the rules for local and global variables in Python?
[...]
On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time.
This is described in section 4.2.2 Resolution of names
When a name is not found at all, a NameError exception is raised. If the current scope is a function scope, and the name refers to a local variable that has not yet been bound to a value at the point where the name is used, an UnboundLocalError exception is raised. UnboundLocalError is a subclass of NameError.
If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.
If a variable name defined in the outer scope is used in a nested scope, it depends on what you do with it in this nested scope:
If you only read a variable, it is the same variable.
If you write to a variable, then Python automatically creates a new, local variable, different from the one in the outer scope.
This local variable prevents access to a variable with the same name in the outer scope.
So writing to a variable don't change its scope, it creates a different, local variable.
You are not able to read this local variable before assigning to it.

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

Local and global references with UnboundLocalError

I don't quite understand why the code
def f():
print(s)
s = "foo"
f()
runs perfectly fine but
def f():
print(s)
s = "bar"
s = "foo"
f()
gives me UnboundLocalError. I know that I can fix this by declaring s as a global variable inside the function or by simply passing s an an argument into the function.
Still I don't understand how python seemingly knows whether or not s is referenced inside the function before the line has been executed? Does python make some sort of list of all local variable references when the function is read into the global frame?
Other answers have focused on the practical aspects of this but have not actually answered the question you asked.
Yes, the Python compiler tracks which variables are assigned when it is compiling a code block (such as in a def). If a name is assigned to in a block, the compiler marks it as local.Take a look at function.__code__.co_varnames to see which variables the compiler has identified.
The nonlocal and global statements can override this.
Yes, Python will look-ahead to recover all variables declared in the local scope. These will then overshadow global variables.
So in your code:
def f():
print(s)
s = "foo"
f()
Python did not find s in the local scope, so it tries to recover it from the global scope and finds "foo".
Now in the other case the following happens:
def f():
print(s)
s = "bar
s = "foo"
f()
Python knows that s is a local variable because it did a look-ahead before runtime, but at runtime it was not assigned yet so it raised and exception.
Note that Python will even let you reference variables that have not been declared anywhere. If you do:
def foo():
return x
f()
You will get a NameError, because Python, when not finding, x as a local variable will just remember that at runtime it should look for a global variable named x and then fail if it does not exist.
So UnboundLocalError means that the variable may eventually be declared in scope but has not been yet. On the other hand NameError means that the variable will never be declared in the local scope, so Python tried to find it in the global scope, but it did not exist.

Immediately enclosing namespace of a top-level function (for the purpose of parsing nonlocal)? [duplicate]

In Python 3.3.1, this works:
i = 76
def A():
global i
i += 10
print(i) # 76
A()
print(i) # 86
This also works:
def enclosing_function():
i = 76
def A():
nonlocal i
i += 10
print(i) # 76
A()
print(i) # 86
enclosing_function()
But this doesn't work:
i = 76
def A():
nonlocal i # "SyntaxError: no binding for nonlocal 'i' found"
i += 10
print(i)
A()
print(i)
The documentation for the nonlocal keyword states (emphasis added):
The nonlocal statement causes the listed identifiers to refer to
previously bound variables in the nearest enclosing scope.
In the third example, the "nearest enclosing scope" just happens to be the global scope. So why doesn't it work?
PLEASE READ THIS BIT
I do notice that the documentation goes on to state (emphasis added):
The [nonlocal] statement allows encapsulated code to
rebind variables outside of the local scope besides the global
(module) scope.
but, strictly speaking, this doesn't mean that what I'm doing in the third example shouldn't work.
The search order for names is LEGB, i.e Local, Enclosing, Global, Builtin. So the global scope is not an enclosing scope.
EDIT
From the docs:
The nonlocal statement causes the listed identifiers to refer to
previously bound variables in the nearest enclosing scope. 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.
why is a module's scope considered global and not an enclosing one? It's still not global to other modules (well, unless you do from module import *), is it?
If you put some name into module's namespace; it is visible in any module that uses module i.e., it is global for the whole Python process.
In general, your application should use as few mutable globals as possible. See Why globals are bad?:
Non-locality
No Access Control or Constraint Checking
Implicit coupling
Concurrency issues
Namespace pollution
Testing and Confinement
Therefore It would be bad if nonlocal allowed to create globals by accident. If you want to modify a global variable; you could use global keyword directly.
global is the most destructive: may affect all uses of the module anywhere in the program
nonlocal is less destructive: limited by the outer() function scope (the binding is checked at compile time)
no declaration (local variable) is the least destructive option: limited by inner() function scope
You can read about history and motivation behind nonlocal in PEP: 3104
Access to Names in Outer Scopes.
It depends upon the Boundary cases:
nonlocals come with some senstivity areas which we need to be aware of. First, unlike the global statement, nonlocal names really must have previous been assigned in an enclosing def's scope when a nonlocal is evaluated or else you'll get an error-you cannot create them dynamically by assigning them anew in the enclosing scope. In fact, they are checked at function definition time before either or nested function is called
>>>def tester(start):
def nested(label):
nonlocal state #nonlocals must already exist in enclosing def!
state = 0
print(label, state)
return nested
SyntaxError: no binding for nonlocal 'state' found
>>>def tester(start):
def nested(label):
global state #Globals dont have to exits yet when declared
state = 0 #This creates the name in the module now
print(label, state)
return nested
>>> F = tester(0)
>>> F('abc')
abc 0
>>> state
0
Second, nonlocal restricts the scope lookup to just enclosing defs; nonlocals are not looked up in the enclosing module's global scope or the built-in scope outside all def's, even if they are already there:
for example:-
>>>spam = 99
>>>def tester():
def nested():
nonlocal spam #Must be in a def, not the module!
print('current=', spam)
spam += 1
return nested
SyntaxError: no binding for nonlocal 'spam' found
These restrictions make sense once you realize that python would not otherwise generally know enclosing scope to create a brand-new name in. In the prior listing, should spam be assigned in tester, or the module outside? Because this is ambiguous, Python must resolve nonlocals at function creation time, not function call time.
The answer is that the global scope does not enclose anything - it is global to everything. Use the global keyword in such a case.
Historical reasons
In 2.x, nonlocal didn't exist yet. It wasn't considered necessary to be able to modify enclosing, non-global scopes; the global scope was seen as a special case. After all, the concept of a "global variable" is a lot easier to explain than lexical closures.
The global scope works differently
Because functions are objects, and in particular because a nested function could be returned from its enclosing function (producing an object that persists after the call to the enclosing function), Python needs to implement lookup into enclosing scopes differently from lookup into either local or global scopes. Specifically, in the reference implementation of 3.x, Python will attach a __closure__ attribute to the inner function, which is a tuple of cell instances that work like references (in the C++ sense) to the closed-over variables. (These are also references in the reference-counting garbage-collection sense; they keep the call frame data alive so that it can be accessed after the enclosing function returns.)
By contrast, global lookup works by doing a chained dictionary lookup: there's a dictionary that implements the global scope, and if that fails, a separate dictionary for the builtin scope is checked. (Of course, writing a global only writes to the global dict, not the builtin dict; there is no builtin keyword.)
Theoretically, of course, there's no reason why the implementation of nonlocal couldn't fall back on a lookup in the global (and then builtin) scope, in the same way that a lookup in the global scope falls back to builtins. Stack Overflow is not the right place to speculate on the reason behind the design decision. I can't find anything relevant in the PEP, so it may simply not have been considered.
The best I can offer is: like with local variable lookup, nonlocal lookup works by determining at compile time what the scope of the variable will be. If you consider builtins as simply pre-defined, shadow-able globals (i.e. the only real difference between the actual implementation and just dumping them into the global scope ahead of time, is that you can recover access to the builtin with del), then so does global lookup. As they say, "simple is better than complex" and "special cases aren't special enough to break the rules"; so, no fallback behaviour.

Categories

Resources