Scope in Python
In python, scope means the domain that a variable can be used. In total, there are five kinds of scope in python:
- local scope, it means the variable is only defined in a function
- enclosed scope, it means the variable is defined outside the inner function, but inside the outer function
- global scope, it means the variable is defined through out the program
- built-in scope, it means some variables are set by some python libraries by default.
- comprehension scope, it is a special scope where variables are only valid inside the comprehension.
For example,
1 | import math |
the output is "global" "enclosedlocal" 3.141592653589793
Why defined such scopes in python?
Because scopes restrict and limit the effective range of variables, avoiding possible name conflicts.
Normally, we only want to use local variables locally, and try to avoid changing global settings. Thus it is necessary to set boundaries between local functions and global environment.
As you can see in the example, I mentioned var1 for many times, each time it was assigned with a different value, and had different tasks. If there is no limit of scopes, the whole code will went to a mess.
Rules of Scope
When program is executing, python finds what your variable name refers to in a specific order according to the scopes. It follows the [[LEGB rule]], search the local scope first, if not exist, then expand to enclosed, global, built-in. In fact, because of the [[Python files execution]] will detect and distribute whether a variable is or is not a local variable, and label it, this rule is seperated:
- for those were labeled as local variables, they will be only treated as local variable.
- for those were labeled as non-local variables, they will not be treated as local variable. But the execution program still start to find these variable at local scope
If and only if you assigned a variable locally, no matter whether this name is used outside the local scope, it will be treated as local variable in this functions.
For example:
1 | var1 = 10 |
the result is
1 | UnboundLocalError: cannot access local variable 'var1' where it is not associated with a value |
`
Yes, although you just attempt to read global variable var1 ahead of the assignment, the program won’t print the value of global var1, instead, rise an UnboundLocalError . That is because early in the first compiling, var1 is labeled as local variable in func1() because the assignment sentence var1 = 20 regardless of its local position. So, when really execute, error comes before the printing output.
To fix it, you must declare that var1 is a global variable:
1 | var1 = 10 |
Then the correct output is:
1 | 10 |
If you want to change global variables locally, or enclosed variables locally, you need to announce those variables with global or nonlocal respectively, like:
1 | var1 = 100 |
the output is 102 (101,203,206) (103,207,210) . It may be a little complex, let’s analyse together:
- steps:
- first I assigned global variable var1 to 100. At this time, a global [[namespace]] is also created: var1 -> object(100) [global]
- then I defined a function, so a function object is also created: func1->function(func1()). All the steps inside function is inside the object func1()[global]
- I assigned
answith the value of func1, so the program will go through func1 - var1 is announced as a global variable, so I can modify it locally
- var1 is add by 1, now var1 -> object(101) [global]
- local variable in func1() is created, and i assigned it value as 2 times value of var1, so a new namespace was created: var2 -> object(202) [local in func1()]
- then I defined func2() inside func1, so func2 -> function(func2())[local in func1()] created, func2 is son of func1()
- var2 is increased by 1, now var2 -> object(203) [local in func1()]
- time to return a tuple. first two value were assigned as var1, var2, like func1_return_value->(101, 203, ?), ? represent func2_return_value.
- dive into func2, I first declared var2 as a nonlocal variable, so var2 is an enclosed variable with respect to func2()
- var2 increased by 3, then var2 -> object(206) [local in func1()]
- func2_return_value -> 206, so func1_return_value->(101, 203, 206)
- so ans->(101, 203, 206)
- var1 is increased by 1, now var1 -> object(102) [global]
- then I print the value of var1, ans, and also execute func1() again to get the third value. currently, the output is: 102 (101,203,206) ___
- again, this time func1_return_value->(103, 206, 210).remember var2 is local in var1, it is a property of var1, so it will remain its value, and being modified each time you call var1.
- so the final out put is: 102 (101,203,206) (103, 206, 210)
So each time you want to distinguish what scope the variable in, check if there is an assignment locally. If not, then gradually expanded to outside.
Special Case
A special case is the comprehension scope, variable in comprehension only valid in comprehension. once the comprehension finished, this variable vanished. Like:
1 | i = 100 |
The result is 100
In contrast, this situation is different:
1 | i = 100 |
This time the result is 4, because the for loop create a local variable i inside my_func(). Since there is not a loop scope, the iis really local through out my_func(), no matter inside or outside the for loop. So at last, iwill point to 4.This i is labeled as local variable initially, so it is nothing to do with the global i.





