Local and global scopes, and try and except statements - Python
Baha Abu-Shaqra, PhD (DTI uOttawa)
Network Engineering (career change)
In this post we look at the Python concepts of local and global scopes, local and global variables, and try and except statements. This post is part six of an eight-part series covering necessary background for the Cisco DevNet and ENAUTO streams certifications.
You may also be interested in Pre-DevNet (Python, Linux, Bash).
Before we dive in, I try to make the world a little better. You're invited to read my letter to uOttawa President?Jacques Frémont about how to easily implement policy reforms to prevent supervisor bullying of uOttawa students: uOttawa President Jacques Frémont ignores university bullying problem. You may also be interested in How to end supervisor bullying at uOttawa.
Local and global scopes
Relative to a function, there is a local scope and a global scope. Parameters and variables assigned in a called function exist in that function’s local scope. A local variable exists in a local scope. Variables assigned outside all functions exist in the global scope. A global variable exits in the global scope. A variable must be either local or global, it cannot be both.
There is only one global scope, created when a program begins. When a program terminates, the global scope is destroyed, and all its variables are forgotten. When a function is called, a local scope is created. Variables assigned in the function exist within the function's local scope. When the function returns, the local scope is destroyed, and the variables are forgotten.
Scopes characteristics
It is best practice not to rely on global variables as a program gets bigger to avoid creating code difficult to debug. Writing functions without global variables is encouraged.
Let's look at some examples.
Local variables cannot be used in the global scope
Consider this program.
def spam():
eggs = 99
print(eggs)
spam()
Run:
99
But,
def spam():
eggs = 99
print(eggs)
spam()
print(eggs)
Run:
99
Traceback (most recent call last):
File "/Users/user/Desktop/python LinkedIn posts/draft.py", line 5, in <module>
print(eggs)
NameError: name 'eggs' is not defined
Running the latter version of the code causes an error because the eggs variable exists only in the local scope created when spam() is called. That local scope is destroyed once the program execution returns from spam, and so there is no longer a variable named eggs. When the program tries to run print(eggs), Python gives us an error that eggs is not defined.
Local scopes cannot use variables in other local scopes
When a function is called, including when a function is called from another function, a new local scope is created. Consider this program:
def spam():
eggs = 99
bacon()
print(eggs)
def bacon():
ham = 101
eggs = 0
spam()
Run:
99
When the spam() function is called, a local scope is created. The local variable eggs is set to 99. Then the bacon() function is called, and a second local scope is created. The local variable ham is set to 101 and the local variable eggs is set to 0. Then bacon() returns and the local scope for that call is destroyed, and with it its eggs variable. The program execution continues in the spam() function to print the value of the spam() function's eggs variable, 99.
Global variables can be read from a local scope
Consider the following program:
def spam():
print(eggs)
eggs = 42
spam()
print(eggs)
Run:
42
42
There is no parameter named eggs or any code that assigns eggs a value in the spam() function, so when eggs is used in spam(), Python considers it a reference to the global variable eggs.
Note - the same variable name can be used for a global variable and local variables in different scopes. But it is best practice to avoid doing this, to reduce the likelihood of confusion.
The global statement
Use a global statement to modify a global variable from within a function. Consider this program.
def spam():
global eggs
eggs = 'spam'
eggs = 'global'
spam()
print(eggs)
The line global eggs at the top of a function tells Python that in this function eggs refers to the global variable. Python will not create a local variable with the name eggs.
Run wabbit run:
spam
What happened? Since we declared eggs global at the top of the spam() function, setting eggs to 'spam' locally actually assigns this value ('spam') to the globally scoped eggs.
The code in a function cannot use a local variable named eggs and then use a global variable named eggs later in that same function.
Four rules to tell whether a variable is in a local scope or a global scope:
Confused? Don't fret.
Functions as "black boxes"
We do not always need to know how a function's code actually works. It is often sufficient to know a function's inputs (parameters) and output. Thinking about a function in this manner is akin to treating the function as a black box.
Recall, it is best practice to write functions without global variables. We usually do not have to worry about the function’s code interacting with the rest of the program.
Exception handling
Getting an error, or exception, in a Python program means the program will crash. But we can instruct Python to detect an error, declare it, and then continue to run.
Consider the following program (zeroDivide.py), it has a divide-by-zero error. We get a ZeroDivisionError when we try to divide a number by zero. The line number (Line 2) in the error message tells us that the return statement in calc() is causing an error.
Let's use a try and except statement to instruct Python to handle the ZeroDivisionError in a more elegant and practical way. We place the code that could potentially have an error in a try clause. When code in a try clause causes an error, the program execution immediately moves to the code in the except clause. Then execution continues as normal.
Let's see how.
We will place the previous divide-by-zero code in a try clause and have an except clause contain code to handle what happens when this error occurs.
The following variation of the previous program has the calc() calls in the try block:
def calc(divideBy):
return 12 / divideBy
try:
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))
except ZeroDivisionError:
print('Error: Invalid argument.')
Run:
6.0
1.0
Error: Invalid argument.
Here, print(spam(1)) is never executed because once the execution jumps to the code in the except clause, it does not return to the try clause, it just continues moving down the program as normal.
*Featured image was created with Mu.
Key references
Automate the Boring Stuff with Python – Al Sweigart free online book (free to read under a CC license)
Automate the Boring Stuff with Python – Al Sweigart YouTube playlist