Skip to content

ይህ ትምህርት ገና ወደ አማርኛ አልተተረጎመም፤ ስለዚህ በእንግሊዝኛ ቀርቧል። የእንግሊዝኛውን ገጽ ክፈቱ

Scope

4 min read

In lesson 5.1 you saw that each call gets its own frame of names, gone when the call ends. That one idea explains a whole family of surprises: names that vanish, a total that isn't the total you meant, and lists that change from inside a function. By the end you'll predict all of them.

Names made in a call stay in the call

Predict what this prints. Look at the last line of the output when you reveal it.

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

def add_up(prices):
    total = sum(prices)
    return total

add_up([120, 80])
print(total)

Python prints

Traceback (most recent call last):
  File "<your code>", line 6, in <module>
    print(total)
          ^^^^^
NameError: name 'total' is not defined

total was made inside the call, so it belonged to that call's frame. When the call ended, the frame and total went with it. The returned value, 200, was never given a name either.

A name made inside a function is : it lives in that call's frame and nowhere else. Names made outside every function are . Where a name can be used is its . To use the result outside, return it and give it a name: bill = add_up([120, 80]).

A local name can even share its spelling with a global one. They're still two names:

Two names called total

Press Next to run one line at a time. Each arrow shows the object a name refers to. Each call gets its own frame of names, below the frame that called it.

total = 0
def add_up(prices):
total = sum(prices)
return total
bill = add_up([120, 80])

Frames, oldest first: global. total refers to an int, 0.

Step 1 of 6

Step 1 of 6. Line 1: total = 0. total now refers to a new int, 0.

Line 4 made a new total in the call's frame. The global total is still 0 at the end.

Reading a global, and assigning one

A function can read a global name. Python looks in the call's frame first, and if the name isn't there, in the global names:

rate = 0.15

def vat(price):
    return price * (1 + rate)

print(vat(1000))

In the editor, press Escape then Tab to move on.

Assigning is different. Predict what happens here:

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

count = 0

def add_visit():
    count = count + 1

add_visit()
print(count)

Python prints

Traceback (most recent call last):
  File "<your code>", line 6, in <module>
    add_visit()
    ~~~~~~~~~^^
  File "<your code>", line 4, in add_visit
    count = count + 1
            ^^^^^
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

Because count is assigned inside the function, Python treats it as local for the whole function. So count + 1 reads the local count, which has no value yet.

Any assignment to a name inside a function makes that name local to the function, from its first line. The error says exactly that: a local variable with no value yet. Python has a way to say "I mean the global one", the global statement:

count = 0

def add_visit():
    global count
    count = count + 1

add_visit()
print(count)

In the editor, press Escape then Tab to move on.

It works, but avoid it. When any function can change a global, you have to read every function to know what a name is, and a function is hard to reuse or test. The clean pattern is pass values in, return values out:

def add_visit(count):
    return count + 1

visits = 0
visits = add_visit(visits)
print(visits)

In the editor, press Escape then Tab to move on.

Lists as parameters

A parameter is a new name for the caller's object, the rule from lesson 4.8. Predict both programs before you look.

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

def change(lst):
    lst.append(9)

a = [1]
change(a)
print(a)
Pick the output

Python prints

[1, 9]

lst and a refer to the same list. append changes that list, so a sees the 9.

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

def rebind(lst):
    lst = [0]

a = [1]
rebind(a)
print(a)
Pick the output

Python prints

[1]

lst = [0] makes a new list and moves the local name lst to it. The caller's list, and a, are untouched.

Step through both. In change, two names in two frames refer to one list:

change(a): one list, two names

Press Next to run one line at a time. Each arrow shows the object a name refers to. Each call gets its own frame of names, below the frame that called it.

def change(lst):
lst.append(9)
a = [1]
change(a)

Frames, oldest first: global.

Step 1 of 5

Step 1 of 5. Line 1: def change(lst):. Nothing in memory changed.

In rebind, only the local name moves:

rebind(a): the local name moves

Press Next to run one line at a time. Each arrow shows the object a name refers to. Each call gets its own frame of names, below the frame that called it.

def rebind(lst):
lst = [0]
a = [1]
rebind(a)

Frames, oldest first: global.

Step 1 of 5

Step 1 of 5. Line 1: def rebind(lst):. Nothing in memory changed.

Changing the object shows through every name; moving a name doesn't. If a function should give back a new list, return it.

ጥያቄ

def reset(scores): scores = []. After data = [5, 7] and reset(data), what is data?

Where Python looks

When you use a name, Python searches four scopes in order, called LEGB, and uses the first match:

ScopeIts names
LocalNames made in the current call
EnclosingNames in a function around this one (functions inside functions, later)
GlobalNames made at the top level of the program
Built-inPython's own names: print, len, sum

መልመጃ

No more global

This works, but add_price changes a global. Rewrite it so add_price(total, price) takes the current total and returns the new one, with no global. It already prints 200; it should still print 200 when you're done.

Your code

total = 0

def add_price(price):
    global total
    total = total + price

add_price(120)
add_price(80)
print(total)

In the editor, press Escape then Tab to move on.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

def add_price(total, price):
    return total + price

total = 0
total = add_price(total, 120)
total = add_price(total, 80)
print(total)

ዋና ዋና ነጥቦች

  • Names made inside a call are local: they live in that call's frame and disappear when it ends.
  • A local name and a global name with the same spelling are two different names.
  • Reading a global works; assigning a name anywhere in a function makes it local, hence UnboundLocalError.
  • Avoid global: pass values in, return values out.
  • A parameter shares the caller's object: changing it shows outside; moving the name doesn't.