Class summary:   Understanding Global
1 First, the code
1.1 Why is global even there?
2 Memory and the Program Dictionary
2.1 When using global
2.2 When creating a new variable inside the function
3 The Big Picture
3.1 But why wasn’t all_  accts  L also global?

Class summary: Understanding Global

Copyright (c) 2017 Kathi Fisler

The create_acct function from yesterday’s code required us to use a Python construct called global. Roughly, one uses global to indicate that a function should update a variable that was defined outside the function. If you want to use Python casually, that explanation should suffice. But for those who really want to understand global, it’s worth understanding it from the perspective of memory and the program dictionary. These notes walk through that explanation.

This material will not be covered on the final

1 First, the code

Here’s the fragment of code we’ll work with for this discussion:

  all_acctsL: list = []

  next_id: int = 1   # the next available id number

  

  def create_acctL(cust_name: str, init_bal: int) -> Account:

      """create new account and add to account list"""

      global next_id

  

      # create account datum with circular references

      a = Account(next_id, init_bal, [])

      c = Customer(cust_name, a)

      a.owners.append(c)

  

      # store account in data structure

      all_accts.append(a)

  

      # advance next_id

      next_id = next_id + 1

      return a

1.1 Why is global even there?

First, let’s understand why the global next_id line is even there. If we comment it out then call create_acctL, Python gives us an error that "next_id is referenced before assignment". This error comes from the line next_id = next_id + 1.

Python can evaluate the right-hand side of this fine – it uses the next_id defined outside the function. But when it comes to storing that value under the name next_id, Python gets stuck. There is no entry for next_id in the program dictionary for the function, and Python can’t be sure that you mean to change the outside variable (given that doing so would impact other parts of the program). It thus throws an error and asks you to be clear on what you want.

As a result, you have to include one of the following two lines within create_acctL:

  global next_id  # says use the variable from outside the function

  next_id: int = 1 # says create a new variable inside the function

It’s worth understanding the differences between these approaches.

2 Memory and the Program Dictionary

What do the program dictionary and memory look like after executing the code, but before calling the function?

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> []

next_id --> 1

create_acctL --> loc 1002       loc 1002 --> function

Now let’s call create_acctL:

  create_acct:("Tina", 150)

2.1 When using global

Assuming we had the global version of next_id inside the function, what would memory and the dictionary look like just before we increment next_id?

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> [loc 1004]

next_id --> 1

create_acctL --> loc 1002       loc 1002 --> function

*** section for function call ***

a --> loc 1004                  loc 1003 --> [loc 1005]

c --> loc 1005                  loc 1004 --> Account(1, 150, loc 1003)

Now, we increment next_id, which changes the value of the variable that’s outside the function section:

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> [loc 1004]

next_id --> 2

create_acctL --> loc 1002       loc 1002 --> function

*** section for function call ***

a --> loc 1004                  loc 1003 --> [loc 1005]

c --> loc 1005                  loc 1004 --> Account(1, 150, loc 1003)

After the call ends, the section for the function call goes away, but the change remains:

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> [loc 1004]

next_id --> 2

create_acctL --> loc 1002       loc 1002 --> function

The next time we call create_acctL, the new account will get id 2, and the next_id will advance to 3.

2.2 When creating a new variable inside the function

Now assume that we had next_id: int = 1 inside the function instead of the global. Now, just before we increment next_id, the program dictionary and memory look a bit different:

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> [loc 1004]

next_id --> 1

create_acctL --> loc 1002       loc 1002 --> function

*** section for function call ***

next_id --> 1

a --> loc 1004                  loc 1003 --> [loc 1005]

c --> loc 1005                  loc 1004 --> Account(1, 150, loc 1003)

Notice there is now a space for next_id within the section for the function call. After we increment the variable, it’s this local copy that changes:

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> [loc 1004]

next_id --> 1

create_acctL --> loc 1002       loc 1002 --> function

*** section for function call ***

next_id --> 2

a --> loc 1004                  loc 1003 --> [loc 1005]

c --> loc 1005                  loc 1004 --> Account(1, 150, loc 1003)

Then the function ends and the local dictionary goes away. Note that the outer next_id has not changed.

Prog Dictionary                 Memory

--------------------------------------------------------------

all_acctsL --> loc 1001         loc 1001 --> [loc 1004]

next_id --> 1

create_acctL --> loc 1002       loc 1002 --> function

Next time we call create_acctL, we will create a new local dictionary with a new next_id variable that gets initialized to 1, then incremented. The new account will also get id 1. There is no shared coordination of ids across calls to the function, which is what we need to get a unique id number for each created account. The global version, in contrast, does that coordination.

3 The Big Picture

Variables are commonly used in programming to communicate across different parts of a program. Here, we need to communicate across calls to create_acctL. To do this, the variable has to be created outside of the function, so that it persists as each call ends. global declares the intent to communicate through next_id.

3.1 But why wasn’t all_acctsL also global?

There is a huge difference between changing the contents of a data structure and reassigning the value associated with a name. The first makes a change within memory; the second makes a change within the program dictionary. We only need to use the global annotation on potential changes to the program dictionary.