Topic 03

Where a Name Lives

Hands-on

A name created inside a function exists only inside that function, and it disappears when the function finishes. This rule is called scope, and it is what lets a hundred functions in one program each use a name like total without ever stepping on one another. It also leads to a simple habit that keeps functions honest.

Think of a hotel room. What you leave in your room is yours, and it is cleared out when you check out. The lobby is shared by every guest, which is exactly why you do not keep your things there.

Local Names

A name assigned inside a function is local to it. It is created when the function runs and gone when it returns. Outside the function, it does not exist. In the program below, total is created inside average. Asking for it afterwards, outside the function, is a NameError: as far as the rest of the program is concerned, there never was a total.

local.py
def average(values):
    total = sum(values)
    return total / len(values)

print(average([18, 22, 25]))
print(total)
Output
21.666666666666668
Traceback (most recent call last):
  File "/Users/you/python-course/local.py", line 6, in <module>
    print(total)
          ^^^^^
NameError: name 'total' is not defined

The fix is not to reach inside the function but to use what it returns. The average came back out through return; that is the only door.

Reading an Outside Name

A function can read a name created at the top level of the file, outside every function. That is convenient, and it has a cost: the function now quietly depends on something it did not ask for. Read it on its own and you cannot tell what it needs.

outside.py
BOILING = 100

def is_boiling(celsius):
    return celsius >= BOILING

print(is_boiling(104))
Output
True

A fixed value like this, written in capitals by convention, is a reasonable thing to read from outside, because it never changes. A value that changes is a different matter, as the next section shows.

Changing an Outside Name Does Not Work the Way It Looks

Assigning to a name inside a function always creates a local name, even if a name with the same spelling exists outside. The outside one is left untouched. In the program below, reset sets its own local count to zero, and the count outside is still 7 afterwards.

shadow.py
count = 7

def reset():
    count = 0

reset()
print(count)
Output
7

Try to change the outside name using its own value, as in count = count + 1, and Python refuses: the name is local, because it is assigned in the function, but it has no value yet when the right-hand side is worked out.

unbound.py
count = 7

def add_one():
    count = count + 1

add_one()
Output
Traceback (most recent call last):
  File "/Users/you/python-course/unbound.py", line 6, in <module>
    add_one()
    ~~~~~~~^^
  File "/Users/you/python-course/unbound.py", line 4, in add_one
    count = count + 1
            ^^^^^
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

Python does have a keyword, global, that lets a function change an outside name, and this book does not use it. The better habit is the one in the next section.

Parameters In, return Out

Give a function what it needs as parameters, and hand back what it makes with return. Then a function is a sealed box: the same inputs always give the same output, and nothing outside changes behind your back. The counter above becomes a function that takes the old count and returns the new one, and the caller decides what to do with it.

add_one.py
def add_one(count):
    return count + 1

count = 7
count = add_one(count)
print(count)
Output
8
Two places a name can live
The file: names created at the top level
visible everywhere in the file, including inside functions, for reading
Inside a function: its own local names
created when it runs, gone when it returns, invisible outside
Common Confusions
  • "A name set inside a function is available after it runs." It disappears when the function returns. Hand it back with return and store the result.
  • "Assigning to an outside name inside a function changes it." It creates a separate local name. The outside one keeps its value.
  • "Globals are simpler." They are simpler for five minutes. After that, every function that touches one has to be understood together with every other.
Why It Matters
  • Local names are why large programs stay understandable: each function can be read, and trusted, on its own.
  • "Parameters in, return out" is the habit that makes Chapter 12's automated tests possible.

Knowledge Check

After a function that sets total = 0 inside it has run, what does print(total) outside it do?

  • Prints 0, the value the function set
  • Raises a NameError, unless total also exists outside
  • Prints None, because the function has finished
  • Prints the last value total had anywhere

With count = 7 outside, what does a function containing count = 0 do to the outside count?

  • Nothing: it makes its own local count, and 7 stays
  • Sets the outside count to 0 as soon as it is called
  • Raises an error, because the name is already taken
  • Deletes the outside count, so it no longer exists

What is the habit this page recommends for functions?

  • Keep shared values at the top so any function can change them
  • Use the global keyword whenever a value must change
  • Print results inside the function instead of returning them
  • Take inputs as parameters and hand results back with return

What does UnboundLocalError mean in count = count + 1 inside a function?

  • The name count was never created anywhere in the file
  • count is local here, and has no value when the right side is read
  • Python cannot add one to a number inside a function
  • The function was called before it was defined further down the file

You got correct