CS61A(14): Mutable Functions

This is the lecture note of CS61A - Lecture 14.

Mutable Functions

Mutable functions' behaviors vary over time. Let's see an example.

  • Let's model a bank account that has a balance of $100
1
2
3
4
5
6
>>> withdraw(25)
75
>>> withdraw(25)
50
>>> withdraw(60)
'Insufficient funds'

Environment Diagram

The implementation is as follows:

1
2
3
4
5
6
7
8
9
10
11
# SOLUTION

def make_withdraw(balance):
"""Return a withdraw function with a starting balance."""
def withdraw(amount):
nonlocal balance
if amount > balance:
return 'Insufficient funds'
balance = balance - amount
return balance
return withdraw

Notice the keyword nonlocal in line 6. We will talk about it in the next section.

Non-local Assignment

However, there is a quirk in Python, see the following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def make_withdraw(balance):
"""Return a withdraw function with a starting balance."""
def withdraw(amount):
# nonlocal balance
if amount > balance:
return 'Insufficient funds'
# balance = balance - amount
return balance
return withdraw

"""
>>> withdraw = make_withdraw(100)
>>> withdraw(25)
100
>>> withdraw(25)
100
>>> withdraw(25)
100
"""

Summary: You need nonlocal if you want to change values declared in parent frames. You don't need nonlocal if you just want to look up values in parent frames of the current environment. (I recommend doing this disc to have better understanding of nonlocal.)

Mutable Values & Persistent Local State

However, mutable values can be changed without a nonlocal statement.

Let's see an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def make_withdraw_list(balance):
b = [balance]
def withdraw(amount):
if amount > b[0]:
return 'Insufficient funds'
b[0] = b[0] - amount # list is mutable ,we don't need 'nonlocal' anymore
return b[0]
return withdraw

"""
>>> withdraw = make_withdraw_list(100)
>>> withdraw(25)
75
>>> withdraw(25)
50
>>> withdraw(30)
20
"""

Example

Let's use an example to finish today's lecture.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def f(x):
x = 4
def g(y):
def h(z):
nonlocal x
x = x + 1
return x + y + z
return h
return g

x = 10
a = f(1)
b = a(2)
total = b(3) + b(4)

Try to work through this program before seeing the answer.

Here is the answer.

1
2
3
4
5
6
7
8
9
>>> a = f(1)
>>> a
<function f.<locals>.g at 0x00000202645AE430>
>>> b = a(2)
>>> b
<function f.<locals>.g.<locals>.h at 0x00000202645AE4C0>
>>> total = b(3) + b(4)
>>> total
22