Skip to content

Variables Scope

Taking as example, this code:

var_scope.py
>>> num = 2
>>> def var_scope(num_fun):
...     print(num_fun)
...     try:
...         print(num)
...         num = 10
...     except BaseException as err:
...         print(err)
>>> var_scope(5)
5
cannot access local variable 'num' where it is not associated with a value

You may notice that print(num_fun) was executed successfully, while print(num) raised an exception, even though num was initialized outside the function. The reason behind this is that when Python compiles the function's body, it identifies num as a local variable because it gets assigned within the function. So, when the function var_scope is called, it can access and print the value of the local variable num_fun. However, when it attempts to access the local variable num, it encounters an issue because b is not bound.

In Python, there's no requirement to declare variables explicitly, but the interpreter assumes that a variable assigned within a function is local. If you want the interpreter to treat b as a global variable and still assign a new value to it within the function, you need to use the global declaration.

var_scope_global.py
>>> num = 2
>>> def var_scope(num_fun):
...     global num
...     print(num_fun)
...     try:
...         print(num)
...         num = 10
...     except BaseException as err:
...         print(err)
>>> var_scope(5)
>>> print(num)
5
2
10

Types of Scope

  1. Module Global Scope Made of names assigned to values outside of any class or function block.
  2. Function Local Scope Made of names assigned to values as parameters, or directly in the body of the function.
  3. Nonlocal Scope The nonlocal scope refer to all those variables that are declared within nested functions. The nonlocal keyword is used to work with variables inside nested functions, where the variable should not belong to the inner function. It lets you declare a variable as a free variable.

Variable Lookup Logic

When defining a function, the Python decides how to access a variable like x within it, following these rules for each situation:

Referenced and Assigned

  1. If there's a global x declaration, the x variable is fetched from and assigned to the global variable x in the module;
  2. When a nonlocal x declaration is present, x is taken from and assigned to the local variable x within the closest surrounding function where x is defined;
  3. If x is either a parameter or assigned a value within the function body, it becomes the local variable.

Just Referenced and is not a Parameter

  1. x will be searched in nonlocal scopes. So, in the local scopes of the enclosing function bodies;
  2. If not in nonlocal scopes, x will be searched in the module global scope;
  3. If not in module global scope, x will be searched in the __builtins__.__dict__;

Comparing bytecodes

bytecodes_compare.py
>>> num = 2
>>> def var_scope_right(num_fun):
...     global num
...     print(num_fun)
...     try:
...         print(num)
...         num = 10
...     except BaseException as err:
...         print(err)
>>> def var_scope_wrong(num_fun):
...     print(num_fun)
...     try:
...         print(num)
...         num = 10
...     except BaseException as err:
...         print(err)
>>> from dis import dis
>>> print(dis(var_scope_right)) # Don't know why is not working
>>> dis(var_scope_wrong) # Don't know why is not working
None

References