## Lesson Three: Local and Global Scope

Now that you are writing your own functions, we need to take some time to carefully understand how variables are used inside and outside of functions. You can initialize and use variables outside of any function, as you have many times already. You can also initialize and use variables inside a function. The location where you first initialize and use a variable is very important, and that location controls the variable's "scope". The concept of scope controls which parts of your code can see and use a variable and how long that variable lasts while your program is running.

### Global Scope

When you initialize and use a variable outside of any function, it is said to have global scope. Most of the variables you have created so far have this global scope. In a simple program that has no functions at all, every variable in that program is global. When a variable has global scope, two important things are true:

1. Global variables can be used from anywhere in your program
2. Global variables will be valid for as long as your program is running

In the example below, we declare and use two variables in the main program - musicTypes and randomIndex. Because these variables were first used outside of any function, they have global scope. The variables are created when they are first initialized with an assignment statement (=), and they will then last until your program ends.

Try It Now

import random
musicTypes = ["Rock","Rap","Country","Jazz","Pop"]
randomIndex = random.randrange(0,len(musicTypes))
print("I like " + musicTypes[randomIndex])

You can run this example to verify that nothing surprising happens with globally scoped variables. These variables behave the way you expect based on your experience to this point. We have simply defined "global" as a new term to describe these types of variables.

### Local Scope

When you create a variable inside a function, it no longer has global scope. Instead, variables defined inside a function have "local" scope. Local variables have two very important differences from global variables.

1. Local variables can only be seen and used from inside that function
2. Local variables will be created when first assigned in the function and destroyed as soon as the function returns
Global variables can be used by code anywhere in your program and will last as long as your program runs. Local variables are created each time your function runs, can only be seen by code inside that function and will be destroyed when the function returns.

Let's re-work our music selection example to put most of the logic inside a function. In the new code below, both musicTypes and randomIndex are first used inside select_music(), and therefore have local scope inside that function. Everything will work fine - until the main code outside the function tries to access one of those variables. Run the code and see what happens!

Try It Now

import random
def select_music():
musicTypes = ["Rock","Rap","Country","Jazz","Pop"]
randomIndex = random.randrange(0,len(musicTypes))
return musicTypes[randomIndex]
print("I like " + select_music())
print(musicTypes) # ERROR - musicTypes does not exist at this point
print(randomIndex) # ERROR - randomIndex does not exist at this point

If you try to access a local variable from outside the function, you will get a run-time error because that variable is not visible to any other part of the program.

I like Jazz
Traceback (most recent call last):
File "code1.py", line 9, in
print(musicTypes)      # ERROR - musicTypes does not exist at this point
NameError: name 'musicTypes' is not defined

When designing your own programs, be sure to understand the difference between global and local scope. Your functions can declare and use local variables that nobody else will see, and they can also use variables that were defined at a global level.

### Using Global Variables from Inside a Function

Once you initialize a global variable, you can use it from anywhere in your code, including inside a function. As you can see in the example below, moving musicTypes out of the function to a global level still works just fine. The statements inside select_music() can read musicTypes as if it was declared locally.

import random

musicTypes = ["Rock","Rap","Country","Jazz","Pop"]       # global variable

def select_music():
randomIndex = random.randrange(0,len(musicTypes))
return musicTypes[randomIndex]                         # reading an existing global variable

print("I like " + select_music())

However, there is a very important situation that should be clearly understood! What happens if you try to assign a new value to a global variable from inside a function? Try running the updated example below. We have declared musicTypes globally and then, inside select_music(), updated the list to contain a new set of values. At the end, we print the global musicTypes list to see what it contains.

Try It Now

import random
musicTypes = ["Rock","Rap","Country","Jazz","Pop"] # global variable
def select_music():
musicTypes = ["Reggae","Disco","Techno","Hip Hop","Soul"] # try to update global variable inside function
randomIndex = random.randrange(0,len(musicTypes))
return musicTypes[randomIndex]
print("I like " + select_music())
print("Global musicTypes:",musicTypes) # show final contents of global variable

Clearly, something strange is going on, as you can see in the example output below.

I like Soul
Global musicTypes: ['Rock', 'Rap', 'Country', 'Jazz', 'Pop']

The select_music() function seems to have updated musicTypes, because it pulled a selection ("Soul") from the updated list. However, after the function returns, printing the global musicTypes list shows it still has the original contents!

By default, the first time you write an assignment statement for a particular variable inside a function, that assignment statement will create a new local variable - even if a global variable of the same name already exists! So, while select_music() was running, there were actually two variables named musicTypes in existence - one at the global level and one at the local level. The function statements will all use the local version, leaving the global version untouched.

It is possible to update a global variable from within a function, but one additional step is needed. Inside your function, add a statement that starts with the "global" keyword, followed by the name of the existing global variable.

musicTypes = ["Rock","Rap","Country","Jazz","Pop"]       # global variable

def select_music():
global musicTypes                                           # declare use of global variable
musicTypes = ["Reggae","Disco","Techno","Hip Hop","Soul"]   # now we are using global variable


Below, we have updated select_music() with a statement the declares global access to the musicTypes variable. Now, when you run the program, there will only be one version of musicTypes throughout the program, and the function will update that global variable.

Try It Now

import random
musicTypes = ["Rock","Rap","Country","Jazz","Pop"] # global variable
def select_music():
global musicTypes # declare use of global variable
musicTypes = ["Reggae","Disco","Techno","Hip Hop","Soul"] # update global variable inside function
randomIndex = random.randrange(0,len(musicTypes))
return musicTypes[randomIndex]
print("I like " + select_music())
print("Global musicTypes:",musicTypes) # show final contents of global variable

### Function Parameters are Local Variables

When your functions define input parameters, those parameters are treated as local variables inside your function! You can update or use them as needed, but those parameter variables will be destroyed when the function ends. If the parameter name happens to match the name of an existing global variable, then you will have two copies of that variable - one global and one local - and your function will use the local version.

In this final example, we have changed select_music() to require an input list of music types. We gave that parameter the same name as the global variable ("musicTypes") - though we could have easily selected a different name. When you run the code, you will notice that the original, global musicTypes variable was not changed by the update inside the function. The local parameter variable was changed instead.

Try It Now

import random
musicTypes = ["Rock","Rap","Country","Jazz","Pop"] # global variable
def select_music(musicTypes): # function parameter is a local variable
musicTypes = ["Reggae","Disco","Techno","Hip Hop","Soul"] # update local variable inside function
randomIndex = random.randrange(0,len(musicTypes))
return musicTypes[randomIndex]
print("I like " + select_music(musicTypes))
print("Global musicTypes:",musicTypes) # show final contents of global variable

### Best Practice - Avoid Using Globals inside Functions

When designing your program, it is generally considered a bad idea to write functions that access or update global variables. Your functions may change the global variable in ways that are not expected by other pieces of code. Similarly, if global values are changed elsewhere, then your function may not behave as designed.

Therefore, professional programmers will usually try to write functions that use input parameters to receive all of the data the function needs to perform a task. That way, the function can't possibly interfere with any other code. It will simply accept input values, perform some task based on those values and return some data to the calling program.

Of course, this "best practice" rule is simply a guideline. You may find situations in your own programs where using global data inside functions is convenient. But the old saying, "Just because you can, doesn't mean you should" is worth remembering when you make design decisions.

Work with Me: Scope Challenge

We've written a short, mysterious program. The program uses a mixture of global and local variables and has one function called mystery(). Carefully study the program below. Can you predict the output of the program before you run it?

Try to figure out how many print() statements will appear in the output window and exactly what each print() statement will show. Once you are confident of your answers, run the code to check the actual results.

Try It Now

a = "alpha"
def mystery(letter):
print(letter)
letter = "omega"
print(letter)
return letter
print(a)
a = mystery(a)
print(a)

Console

Did you predict the correct output? If you need help understanding what happened, click on the "Show Solution" tab for a description of each line.