Skip to main content

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:
        x = 10
        This creates an entry for x in the program directory that cannot be updated using :=.
      • Mutable declaration:
        var x = 10
        This creates an entry that you can later update with :=.
    • Attempting to update an immutable variable (e.g., writing x := 20 when x was declared with x = 10) will yield an error.
  • 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 (or nonlocal in nested functions).

C. Interactive Discussion

  • Question: “Imagine you have a variable x. In Pyret, what happens if you write x = 10 and later try to do x := 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 :=.
  • Visualize with the Program Directory:
    • In Pyret, the directory records an immutable binding for x if declared with x = 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).

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.
  • 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.

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 or nonlocal 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.
  • Follow-up:
    • Ask: “How can you fix this so that inner() updates the y from outer()?”
      • Answer: Use nonlocal y in inner().

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 :=.

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.
    • 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 declare global x.
  • Interactive Exercise:
    • Ask students to compare what happens in the following cases:
      • In Pyret:
        y = 20
        y := y + 10
        y
        versus trying to write
        y = 20
        y = 30
        (which causes an error).
      • In Python:
        y = 20
        def f():
        y = 30
        return y
        print(f())
        print(y)
  • 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.
  • Python:
    • Assignment (=) both creates and updates bindings.
    • In inner scopes, assignment creates a new local binding unless global or nonlocal is used.
    • Python’s program directory (namespace) behaves differently: local and global directories exist simultaneously.