Skip to content

Instantly share code, notes, and snippets.

@jasonpr59
Last active August 29, 2015 14:13
Show Gist options
  • Save jasonpr59/6606e7cce706010e1814 to your computer and use it in GitHub Desktop.
Save jasonpr59/6606e7cce706010e1814 to your computer and use it in GitHub Desktop.
Scope Examples

Scope Explanation

(This Gist can be accessed at http://goo.gl/ZYAZkC.)

Introduction

In Python we use variable to refer to objects. In other words, a variable is a name for an object.

This Gist is an explanation of how Python creates variable names, and how it decides what object a variable name refers to.

Scopes

Python uses scopes to keep track of variable names. A scope is a mapping from 'variable name' to 'object that name refers to.' (Technically, it's a mapping from 'variable name' to 'reference to object that name refers to', since object exist independently of the names we give them.)

TODO(jasonpr): Put a picture!

When you write an assignment statement, koalas_in_back_yard = 50, Python adds an entry to a scope that points the name koalas_in_back_yard to an object, 50. But, how does Python decide which scope should receive that entry?

The Global Scope

If your assignment statement is written outside of all function definitions, then assignments affect the global scope. The global scope is created when you start the Python shell, and stays around for the duration of your session.

Local Scopes

If your assignment statement is written inside of a function definition, then assignments affect a local scope (i.e. a function scope). Every time the enclosing function is run, Python creates a fresh scope for that function. When the function completes (say, when it is returned from), the local scope is destroyed. (Read about closures if you want to learn about zombie-like local scopes that stick around after their function returns!)

Globals and Locals

Let's add one variable to the global scope, and make a function with a local variable, too. When you run this example, you should speculate about how Python was able to find the global variable fav_rapper inside of a function.

fav_rapper = 'Eminem'

def print_favorites():
    print 'Favorite rapper:', fav_rapper
    fav_person = 'Shakira'
    print 'Favorite person:', fav_person

print_favorites()

Output:

-> Favorite rapper: Eminem
-> Favorite person: Shakira

Name Resolution Order: Local Scope First

When we ask Python to lookup a variable, Python must decide which scope(s) to look in.

If we are not in a function, Python always looks in the global scope. If it can't find the variable name, it gives up! If we are in a function, Python looks inside the local scope. If it finds the variable name, then it declares success and uses the object associated with that name. But, critically, if it can't find the name in the local scope, it tries the global scope next. Only if the name is also missing from the global scope does Python give up.

fav_color = 'green'

def tell_me_about_jimmy():
    name = 'James Smith'
    fav_color = 'fuchsia'
    print name + "'s favorite color is", fav_color

tell_me_about_jimmy()

print 'My favorite color is still', fav_color

Output:

-> James Smith's favorite color is fuchsia

Building the Local Scope

Every function is called, Python builds up a new local scope for that function. Here's how it works:

  1. Create a totally empty scope.
  2. Make an entry in the scope for every parameter name. Each parameter should point to the object that was supplied as an argument.
  3. Scan through the code, without executing any code yet. Look for all variable names in the body of the function that get a value assigned to them. (In the introduce_person function, we find just one-- punctuation.) Add these names to the scope, but make note that we don't know their associated objects, yet. We will add in their objects when we actually execute the assignment statement.

At this point, the scope is built, and the body of the function can be executed.

Below, we can see that the name fav_car_maker is added to the local scope for calls of introduce_person. When it's looked up in the function's print statement, the local scope is checked first. That's how the parameter is correctly looked found.

fav_car_maker = 'Tesla'

def introduce_person(name, fav_car_maker):
   punctuation = "!"
   print name + "'s favorite car maker is", fav_car_maker + punctuation

introduce_person('Brandon', 'Ford')
introduce_person('Carlos', 'Audi')

print 'My favorite car maker is still', fav_car_maker
-> Brandon's favorite car maker is Ford!
-> Carlos's favorite car maker is Audi!
-> My favorite car maker is still Tesla

When Does Python Learn About the Local Name?

In step 3 above, Python adds some names to the scope... even before those names are first assigned to! If we try to resolve a name that doesn't yet have a value, Python will not be happy:

conflicting_name = 123

def mess_with_conflicting_names():
    print conflicting_name
    conflicting_name = 456

mess_with_conflicting_names()

It doesn't work! We get the following error message:

Traceback (most recent call last):
  File "scope.py", line 7, in <module>
    mess_with_conflicting_names()
  File "scope.py", line 4, in mess_with_conflicting_names
    print conflicting_name
UnboundLocalError: local variable 'conflicting_name' referenced before assignment

Binding a Name

Many operations bind a name.

  • Assignments: jasons_study_hours = [3, 2, 0, 8, 1] binds jasons_study_hours.
  • Function definitions: def get_money(): ... binds get_money.
  • Passing parameters: introduce_person('Bob', 'BMW') binds name and fav_car_maker.
  • Import statements: import string binds string.
  • For more, see https://docs.python.org/2/reference/executionmodel.html.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment