Scope
Scope decides which variables are visible from a given line of code. Getting scope right avoids a whole class of confusing bugs.
Local scope
A variable created inside a function exists only inside that function.
def demo():
inner = "I live inside demo"
print(inner)
demo()
try:
print(inner)
except NameError as e:
print("Error:", e)Each call gets fresh local variables. When the function returns, they are gone.
Global scope
A variable created at the top level of a file is global. Functions can read it.
rate = 18
def with_tax(amount):
return amount * (1 + rate / 100)
print(with_tax(100))Assigning creates a new local
If you assign to a name inside a function, Python treats it as a new local variable, even if a global with the same name exists.
count = 0
def increment():
count = 1 # this is a new local, the global is untouched
print("inside:", count)
increment()
print("outside:", count)The global keyword
To modify a global from inside a function you must declare it.
count = 0
def increment():
global count
count += 1
increment()
increment()
print(count)Use this sparingly. Functions that silently change globals are hard to reason about. Prefer returning a value and assigning it outside.
Enclosing scope and nonlocal
Functions can be defined inside other functions. The inner function can read the outer function's variables. To assign to them, use nonlocal.
def counter():
n = 0
def tick():
nonlocal n
n += 1
return n
return tick
c = counter()
print(c(), c(), c())This pattern is called a closure: tick remembers the n from the call to counter that created it.
The LEGB rule
When Python sees a name, it looks in this order:
- Local: the current function
- Enclosing: any outer functions
- Global: the module
- Built in: names like
print,len,range
def outer():
x = "enclosing"
def inner():
print(x)
inner()
x = "global"
outer()Shadowing built ins
You can name a variable list or sum, but then the built in is hidden for the rest of that scope. Editors will warn you; listen to them.
def demo():
sum = 10 # shadows the built in sum()
print(sum)
demo()
print(sum([1, 2, 3])) # the built in is still fine out herePractice
- Write a function that tries to print a variable defined only inside a different function. Read the error.
- Write a
make_greeter(greeting)function that returns an inner function which greets any name with that greeting. - Fix this code so it works without
global: a function that "increments" a score, by returning the new score instead.
