Skip to the content.

Code 401 Class 07 Reading Notes

Python Scope

Python provides two keywords that allow you to modify the content of global and nonlocal names:

The global Statement

With the global statement, you can define a list of names that are going to be treated as global names.

The statement consists of the global keyword followed by one or more names separated by commas.

A successful example:

>>> counter = 0  # A global name
>>> def update_counter():
...     global counter  # Declare counter as global
...     counter = counter + 1  # Successfully update the counter
...
>>> update_counter()
>>> counter
1
>>> update_counter()
>>> counter
2
>>> update_counter()
>>> counter

As shown above, you add the statement global counter to the body of the function right before you try to change counter. From here, you are mapping the name counter in the function scope ot the same name in the global or module scope. Now, you can freely modify counter inside update_counter(). All changes will reflect in the global variable.

Here’s a better example of the global counter:

>>> global_counter = 0  # A global name
>>> def update_counter(counter):
...     return counter + 1  # Rely on a local name
...
>>> global_counter = update_counter(global_counter)
>>> global_counter
1
>>> global_counter = update_counter(global_counter)
>>> global_counter
2
>>> global_counter = update_counter(global_counter)
>>> global_counter
3

This implementation of update_counter()defines counter as a parameter and returns its value augmented buy 1 unit every time the function is called. This way, the result of update_counter() depends on the counter you use as an input and not on the changes that other functions(or pieces of code) can perform on the global variable, global_counter.

>>> def create_lazy_name():
...     global lazy  # Create a global name, lazy
...     lazy = 100
...     return lazy
...
>>> create_lazy_name()
100
>>> lazy  # The name is now available in the global scope
100
>>> dir()
['__annotations__', '__builtins__',..., 'create_lazy_name', 'lazy']

When calling create_lazy_name, you’re also creating a global variable called lazy.Now, the name lazy is available in the global Python scope. If you inspect the global namespace using dir(), then you’ll see that lazy appears last in the list.

CAUTION, this can be a dangerous practice that can lead to buggy code.

>>> name = 100
>>> dir()
['__annotations__', '__builtins__',..., '__spec__', 'name']
>>> global name
>>> dir()
['__annotations__', '__builtins__',..., '__spec__', 'name']

The use of a global statement like a global name doesn’t change anything in your current global scope, as you can see in the output of dir(). The variable name is a global variable whether you use global or not.

The nonlocal Statement

Similar to global names, nonlocal names can be accessed from inner function, but not assigned or updated. If you want to modify them, then you need to use a nonlocal statement. With `nonlocal statement, you can define a list of names that are going to be treated as nonlocal.

The statement consists of the nonlocal keyword followed by one or more names separated by commas. These names will refer to the same names in the enclosing python scope.

Example:

>>> def func():
...     var = 100  # A nonlocal variable
...     def nested():
...         nonlocal var  # Declare var as nonlocal
...         var += 100
...
...     nested()
...     print(var)
...
>>> func()
200

With the statement nonlocal var, you tell Python that you’ll be modifying var inside nested(). Then, you increment var using an augmented assignment operation. This change is reflected in the nonlocal name var, which now has a value of 200.

Unlike global, you can not use nonlocal outside of a nested or enclosed function To be more precise, you can’t use a nonlocal statement in either the global scope or in a local scope.

Example:

>>> nonlocal my_var  # Try to use nonlocal in the global scope
  File "<stdin>", line 1
SyntaxError: nonlocal declaration not allowed at module level
>>> def func():
...     nonlocal var  # Try to use nonlocal in a local scope
...     print(var)
...
  File "<stdin>", line 2
SyntaxError: no binding for nonlocal 'var' found

The nonlocal statement only works inside an inner or nest function.

You you also can not use nonlocal to create lazy nonlocal names. Names must already exist in the enclosing Python scope if you want to use them as nonlocal names. This means that you can’t create nonlocal names by declaring them in a nonlocal statement in a nested function.

Example:

>>> def func():
...     def nested():
...         nonlocal lazy_var  # Try to create a nonlocal lazy name
...
  File "<stdin>", line 3
SyntaxError: no binding for nonlocal 'lazy_var' found

In the above example, trying to define a nonlocal name using nonlocal lazy_var, Python immediately raises a SyntaxError because lazy_var doesn’t exist in the enclosing scope of nested().

Things I want to know more about

For global variable, what do the changes look like in the global variable.

<—BACK