Prev: Arguments       |       Next: Functions as first-class objects

# Variable Scope¶

### What is "variable scope"¶

• Simply speaking, a "variable scope" in Python is the namespace where a name-object binding is valid and usable.
• Variable scope becomes vital when practicing modular programming techniques where the programmer must clearly understand how names are bound (or not) to various objects in different parts of the code.
• Let's see an example:
In [ ]:
def addcon(x,y):
asum = x+y
return asum

a = 5
b = 10
c = addcon(a,b)
print(c)

• When executing the above code two distinct namespaces are developed (left panel in the figure below):
• global: by global we refer to the namespace of the main program. This contains the names 'addcon', 'a', 'b' and their object bindings. A variable 'is global' ('has global scope') when it belongs to the global namespace and, therefore, it can certainly be used in the main program (and elsewhere as explained further below)
• local: by local we refer to the namespace created by a function while it is executed. In our example the addcon local namespace includes the names 'x', 'y', 'asum' and their object bindings. A variable 'is local' ('has local scope') when it belongs to a function local namespace and, therefore, it can certainly be used in that function

• After the addcon() execution is over (right panel in the figure above) its local namespace siezes to exist, but the global namespace is now larger: the name 'c' has been added bound to the object '15' that the function returned

### Rules¶

• Python applies certain simple rules to define the scope of variables. Let's see them below (remember always that by 'variable' in Python we always mean a name-object binding)

1) Variables belong where the binding is first constructed: if it is constructed in the main program the variable is global. If in a function then the variable is local in that function. 'Constructed' in this context means that a name-object binding was established by an assignment command, such as: x=5, y='spam', alist=[], adict={}, etc.

In [ ]:
def addcon(x,y):
asum = x+y       # asum is a local var 'constructed' in the function by this assignment
return asum

a = 5                # a and b are global vars 'constructed' in the global namespace of the main program
b = 10
c = addcon(a,b)      # c is also a global var 'constructed' by this assignment
print(c)


2) If a variable is used somewhere but NOT constructed there: then Python interpreter looks up for this variable at the higher levels of a scope hierarchy. If the variable is identified at some level then it is assigned the scope of that level and computation continuous. If it is not identified at any level then a 'NameError' exception is raised and execution is terminated.

3) Scope hierarchy: Python looks for variable scope following the 'LEGB' hierarchy, which means:

• Local: a variable is sought for first in the local namespace (in the function) where it is used
• Enclosing: the next level is any enclosing (container) function containing the function where the variable is used
• Global: the upper level is the global level of the main program
• Built-ins: finally Python searches for a variable in the built in names that the language reserves (for example, len, sum, class, etc.)

When an module is imported then the namespace is further extended and prefixes are commonly used to correctly identify the imported namespaces.

#### Examples¶

In [4]:
def func1():

def func2():
x = 300           # x is local in func2() because of this assignment
return x

x = 200               # another x is local in func1() because of this assignment
a = func2()
b = x
return a, b

x = 100                   # another x is global; is different from x's in func1() and func2()
print(func1())
print(x)

(300, 200)
100

In [1]:
def func1():

def func2():
x = 300+a           # a is NOT local in func2() but identified in the higher 'Enclosing' level
return x

x = 100+y               # y is NOT local in func1() but identified in the higher 'Global' level
a = 400
b = func2()
return a, b

x = 100
y = 300
print(func1())
print(x)

(400, 700)
100

In [2]:
def func1():

def func2():
x = 300+a
c = 700             # c is local here and NOT available in higher levels of the hierarchy
return x

x = 200+y
a = c                   #--- this returns: 'NameError: name 'c' is not defined'
b = x
return a, b

x = 100
y = 500
print(func1())
print(x)
print(c)                    #--- this returns: 'NameError: name 'c' is not defined'


4) global and nonlocal: There are two 'declarations' that help override the above rules when necessary:

• global: when declaring a var as global in a function, it's scope becomes automatically 'global'
• nonlocal: when declaring a var as nonlocal in an enclosed function, it's scope becomes automatically local in the function above (the enclosing function)

See the examples:

In [3]:
def func1():

def func2():
nonlocal x        # x is declared as nonlocal; thus, it is identical to 'x' in the higher enclosing func1()
x = 300
return x

x = 200               # because of 'nonlocal' now this 'x' is identical to the 'x' var in enclosed func2()
a = func2()
b = x                 # Now b gets the value '300'. Why?
return a, b

x = 100                   # this x is global and different from x in func1()
print(func1())
print(x)

(300, 300)
100

In [8]:
def func1():
global x              # x is declared as nonlocal; thus, it is identical to the global 'x' in main program

def func2():
x = 300
return x

x = 200
a = func2()
b = x                 # Now b gets the value '200'. Why?
return a, b

x = 100                   # because of 'global' now this 'x' is identical to the 'x' var in func1()
print(func1())
print(x)                  # Now x gets the value '200'. Why?

(300, 200)
200


5) Mutable vs. immutable object behavior: when thinking about scope always recall the different behavior of mutable/immutable objects when assigned values in a function:

• Immutable objects (integerss, strings,..): any assignment is considered as construction and the variable becomes local (a new copy is created). Any global variable of the same name stays intact.
• Mutable objects (lists, dictionaries,..): assignment of a new value to a member is not considered construction. Any global variable of the same name is affected due to shared object reference.
In [10]:
def myfunc():
a = 31        # a is immutable; assignment constructs a new local obect in myfunc()
b[0] = 'K'    # b is mutable; changing a member value does NOT construct new copy
return

a = 10
b = [1,2,3]
myfunc()
print(a, b)       # global a is not affected by myfunc(); list b is affected because of shared object reference

10 ['K', 2, 3]


. Free learning material
. See full copyright and disclaimer notice