Variable Scoping and Assignment in Python vs. Pyret
1. Introduction & The Program Directory (10 minutes)
A. The Program Directory as a Mental Model
- Concept Recap:
- The “program directory” is our mental model of how a language keeps track of variable names and their associated values.
- In Pyret, when you write a declaration, an entry is added to this directory.
- In Python, a similar model exists; we sometimes refer to this as the namespace or program directory.
B. Key Differences in Declaration and Assignment
-
Pyret:
- Variables are declared as either immutable or mutable.
- Immutable declaration:
This creates an entry for
x = 10
x
in the program directory that cannot be updated using:=
. - Mutable declaration:
This creates an entry that you can later update with
var x = 10
:=
.
- Immutable declaration:
- Attempting to update an immutable variable (e.g., writing
x := 20
when x was declared withx = 10
) will yield an error.
- Variables are declared as either immutable or mutable.
-
Python:
- There is no separate syntax for declaration versus assignment. The operator
=
both creates a new binding and updates an existing one. - In a function, assigning to a variable creates a new local binding unless you declare it as
global
(ornonlocal
in nested functions).
- There is no separate syntax for declaration versus assignment. The operator
C. Interactive Discussion
- Question: “Imagine you have a variable x. In Pyret, what happens if you write
x = 10
and later try to dox := 20
? What must you do to allow x to be updated?” - Expected Answer:
- You must declare x as mutable using
var x = 10
to later update it with:=
.
- You must declare x as mutable using
- Visualize with the Program Directory:
- In Pyret, the directory records an immutable binding for
x
if declared withx = 10
; it cannot be updated. - In Python, the same name x in a given scope will be updated with each assignment (unless shadowed by a local binding).
- In Pyret, the directory records an immutable binding for
2. Variable Scoping in Python (20 minutes)
A. Global vs. Local Bindings in Python
-
Example 1: Global Assignment
x = 10 # x is bound in the global directory
def show_x():
return x
print(show_x()) # prints 10- Discussion:
- The function
show_x
looks up x in the global directory.
- The function
- Discussion:
-
Example 2: Local Binding and Shadowing
x = 10
def change_x():
x = 20 # This creates a new local variable x, shadowing the global x
return x
print(change_x()) # prints 20
print(x) # prints 10, because global x remains unchanged- Interactive Question:
- “Why doesn’t change_x() update the global x?”
- Answer: Because assignment in the function creates a new local binding for x.
- Interactive Question:
B. Using Global and Nonlocal Declarations
- Example 3: Updating the Global Variable
x = 10
def update_global_x():
global x # declares that x refers to the global variable
x = 50
update_global_x()
print(x) # prints 50 - Example 4: Nested Functions and nonlocal
x = 10
def outer():
x = 20
def inner():
nonlocal x # refers to x in the outer function
x = 30
inner()
return x
print(outer()) # prints 30
print(x) # prints 10 (global x unchanged) - Discussion:
- Use the program directory model: In Python, each function call creates its own local “directory.”
- Assignment in an inner function creates a new binding unless you specify
global
ornonlocal
to refer to an outer binding.
C. Interactive Exercise
- Task:
- Provide the following code and ask students to predict the output:
y = 100
def outer():
y = 200
def inner():
y = y + 50 # What happens here?
return y
return inner()
print(outer()) - Expected Discussion:
- This will raise an UnboundLocalError because Python sees
y = y + 50
in inner() and treats y as a local variable which is referenced before assignment.
- This will raise an UnboundLocalError because Python sees
- Provide the following code and ask students to predict the output:
- Follow-up:
- Ask: “How can you fix this so that inner() updates the y from outer()?”
- Answer: Use
nonlocal y
in inner().
- Answer: Use
- Ask: “How can you fix this so that inner() updates the y from outer()?”
3. Variable Scoping in Pyret (20 minutes)
A. Immutable vs. Mutable Declarations in Pyret
- Example 1: Immutable Declaration
x = 10
# x is declared immutable. Attempting to update:
# x := 20 (This would cause an error.) - Example 2: Mutable Declaration
var x = 10
x := x + 5 # Now x becomes 15.
x- Discussion:
- Explain that in Pyret, the program directory holds a binding for x. If x is declared immutably (with
x = 10
), it cannot be changed. - Using
var x = 10
creates a mutable entry that can later be updated with:=
.
- Explain that in Pyret, the program directory holds a binding for x. If x is declared immutably (with
- Discussion:
B. No Accidental Shadowing in Pyret
- Explanation:
- In Pyret, once a name is declared in a scope, re‑declaring that same name in the same or nested scope is forbidden.
- This means there is no accidental shadowing—updates are explicit using
:=
.
C. The Program Directory in Pyret vs. Python
- Discussion using the Program Directory Model:
- Pyret:
- When you write
var x = 10
, the program directory records an entry for x as mutable. - Updating x with
x := 20
directly changes that entry. - If you try to re‑declare x (e.g.,
x = 50
), Pyret raises an error because x is already in the directory.
- When you write
- Python:
- When you write
x = 10
at the global level, x is added to the global directory. - Inside a function, writing
x = 50
creates a new local entry in that function’s directory, leaving the global unchanged unless you declareglobal x
.
- When you write
- Pyret:
- Interactive Exercise:
- Ask students to compare what happens in the following cases:
- In Pyret:
versus trying to write
y = 20
y := y + 10
y(which causes an error).y = 20
y = 30 - In Python:
y = 20
def f():
y = 30
return y
print(f())
print(y)
- In Pyret:
- Ask students to compare what happens in the following cases:
- Discussion:
- Have students describe, using the program directory model, how each language’s approach avoids (or does not avoid) accidental variable changes.
4. Wrap-Up (10 minutes)
A. Recap Key Points
- Pyret:
- Declarations must be marked as mutable (using
var
) if they are to be updated. - The operator
=
is only for declaring a name;:=
is for updating an existing binding. - The program directory in Pyret prevents accidental shadowing by not allowing re‑declaration.
- Declarations must be marked as mutable (using
- Python:
- Assignment (
=
) both creates and updates bindings. - In inner scopes, assignment creates a new local binding unless
global
ornonlocal
is used. - Python’s program directory (namespace) behaves differently: local and global directories exist simultaneously.
- Assignment (