Non-local references in procedures
There is an important subtlety in the way names are handled in the environment created by a function call. When a value that is not related in the local environment is referenced, then it is create up in the chain of base environments. So, as we have look, it is ?ne to have
a = 2
def b():
return a
When a name is assigned inside a method, a new relation is created for it in the environment associated with the current call of that method. So, it is ?ne to have
a = 2
def b():
a = 3
c = 4
return a + c
Both assignments cause new bindings to be made in the local variables, and it is those bind ings that are used to supply values in the return statement. It will not modify a in the global environment.
But here is a program fragment that gives trouble:
a = 3
def b():
a = a + 1
print a
It seems completely understandable, and you might expect b() to return 4. But, instead, it produces an error. What is going on?? It each one has to do with when Python decides to add a binding to the local function. When it takes this method de?nition, it sees that the name a occurs on the left-hand-side of an assignment expression, and so, at the very start, it gives a new entry for a in the local variables, but without any number related to it. Now, when it is time to compute the statement
a = a + 1
Python starts by evaluating the expression on the right hand side: a + 1. When it tries to look up the name a in the procedure-call environment, it search that a has been included to the environment, but has not yet had a number given. So it gives an error.
In Python, we can write code to increment a number named in the global variable, by using the global declaration:
a = 3
def b():
global a
a = a + 1
print a
>>> b()
4
>>> b()
5
>>> a
5
The statement global a asks that a new binding for a not be made in the procedure-call environment. Now, all instances to a are to the related in the module's environment, and so this procedure actually modifies a.
In Python, we can only make assignments to names in the procedure call environment or to the module variable, but not to names in intermediate variables. So,
def outer():
def inner():
a = a + 1
a = 0
inner()
In this example, we get a exception, because Python has build a new binding for a in the environment for the invoke to inner. We could really like inner to be able to see and modify the a that belongs to the scope for outer, but there is no way to manage this. Some other programming languages, such as procedure, offer more flexible grained control over how the scoping of variables is handled.