The Global and Nonlocal Keywords in Python
Why do they exist?
Using global variables is considered bad practice (if this comes as a surprise to you, read this explaining why).
To discourage their use, Python needs you to explicitly state when you want to use them.
Python is Different in Scoping
You have to explicitly state because Python searches for variables referred to in the local scope first, then the enclosing scope, then the global scope, and uses the first one it encounters.
This is in contrast to many other languages that search top-down for variables i.e. first globally, then locally. For example, in C++ if a global variable is already present, any changes we make to it inside a function will reflect in its original space:
Whereas in Python, even if a variable with the same name exists outside the function, the assignment operator will create a new one locally:
Globally, the value of v remains unchanged because Python created a new local v, as if no other v existed, and when foo() exited v was garbage-collected away.
Using Global
Bare in mind global is not required when you’re just trying to read. You are allowed to only refer to variables defined above the current scope. Python will give you the value of the first v it finds going up, if no v was present locally.
Whenever you want to modify a global variable inside of a function, define it with global just once in the current scope before doing anything with it, like so:
And if there wasn’t a global variable v defined already, Python will use the assignment operator to create one in the global scope.
That means you can define a new global variable from any nested scope, not just from the global scope itself.
Now onto nonlocal. But first…
The Enclosing Scope
If a function is defined inside of another function, the inner function can read variables present in the outer function. So we say the scope of the outer function is also the enclosing scope of the inner function.
By the way, this pattern of nesting a function inside another function, and then the inner function having access to the outer function’s variables (if any), is called a closure (also common in JavaScript). Python decorators are based on closures. They’re also used to implement data privacy in JavaScript. Here’s an easy explanation of closures.
The cool thing about enclosing scopes is that if we were to return the inner function as an object, the enclosing variables will remain intact on subsequent calls to inner, even though outer() had finished and its local scope was destroyed when we used it to get inner() in the first place.
But we would really be taking advantage of enclosing scopes only if we did something with v apart from just reading it, like incrementing and keeping track of the number of times inner() was called.
Reading, as I said, doesn’t require any keywords. Modifying v, however, from inner() won’t work simply by assigning to it (that would create a local variable) nor by global (that would create a global variable).
Therefore, nonlocal was introduced to allow modification of variables present in the enclosing scope, which otherwise wasn’t possible.
Using Nonlocal
From the documentation,
The
nonlocalstatement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals.
Of course there can be multiple enclosing scopes, so the nearest one going up will be your reference.
Let’s try the function count thing now.
Notice how v in the global scope remained untouched. To drive the point home, let’s do two levels of nesting:
Invoking outer() returns the middle function object, which we invoke by the second set of parenthesis to get the inner function object. Here, both outer() and middle() make up the enclosing scope of inner(). But nonlocal will refer to the nearest one.
To learn more about scoping in Python in detail, check out this great piece.
Things to Remember
- Read variables however you like.
- Use global if you wish to modify a variable’s value for good. Again, that’s not good design.
- Whatever you do after defining a variable with global, you’re doing it in the zero indentation of the script.
- Use nonlocal to modify variables in the enclosing scope of a function.
- Nonlocal is not global, and it’s obviously not local. It’s the thing in between.
This has been my first Medium story. Feel free to point out any mistakes or to add something I missed.
Peace☺
