Skip to content
Home » Local, Nonlocal, Global and Built-in Variable Scope

Local, Nonlocal, Global and Built-in Variable Scope

Spread the love

Today we’ll discuss scopes. To start with, what is scope? Well, to keep it simple scope is the part of your program where a variable is visible.

variable scope

The Four Scopes

Generally, there are four scopes in Python:

– Local scope

– Nonlocal scope

– Global scope

– Built-in scope

Local and Nonlocal Variable Scope

Local scope is where the variable is created. If it happens directly in the module, then the module is its local scope. If in a function, then the function is its local scope.

But if we define a nested function and inside the function we define a variable, then the nested function is the variable’s local scope and the enclosing function is the nonlocal scope. Have a look:

def fEnclosing(): 
    x = 10
    def fNested():
        y = 5

Here y is in the local scope of the nested function and x is in the local scope of the enclosing function. At the same time x is in the nonlocal scope of the nested function.

The general rule is that in local scope you have access to nonlocal scope but not vice-versa. So, in the nested function x is accessible, but y is not accessible in the enclosing function. Have a look:

>>> def fEnclosing(): 
...     x = 10
...     def fNested():
...         y = 5
...         print(x + 5)
...     fNested()
... 
>>> fEnclosing()
15

And now let’s try to access y in the enclosing function:

>>> def fEnclosing(): 
...     x = 10
...     print(x + y)
...     def fNested():
...         y = 5
... 
>>> fEnclosing()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in fEnclosing
NameError: name 'y' is not defined

Although we can nest functions at as many levels as we want to, it generally isn’t good practice. The code gets difficult to read. Anyway, just theoretically imagine a hierarchy of functions like that:

def fEnclosing(): 
    x = 10    
    def fNested_level1():
        y = 5
        def fNested_level2():
            z = 3
            def fNested_level3():
                w = 1

So, w is local in the level 3 function, z is local in level 2, y is local in level 1 and x is local in the enclosing function. Inside the innermost level 3 function z, y and x are all in nonlocal scope and if the program has to find them, the search goes in the direction from inner to outer.

This looks awkward, so you should avoid it. One level of nesting is quite common but we shouldn’t use more levels of nesting, for the sake of our mental health.

Your Panda3D Magazine

Make Awesome Games and Other 3D Apps

with Panda3D and Blender using Python.

Cool stuff, easy to follow articles.

Get the magazine here (PDF).

Global Variable Scope

Then we have global scope. If you define a variable directly in a module, it’s in global scope. Have a look:

>>> n = 20
>>> def fEnclosing(): 
...     x = 10 
...     print(x + n)
...     def fNested():
...         y = 5
...         print(y + n)
...     fNested()
... 

Here n is in global scope, which, at the same time is the local scope for the module.

y and z are defined in the functions. We can access variables defined in global scope from any function at any level of nesting. It’s global, which means accessible in the whole module. But again, we can’t access the variables defined in the functions from global scope.

Let’s call the outer function:

>>> fEnclosing()

Here’s the output:

30
25

And now let’s try to access the variables defined in the functions from global scope:

>>> n = 20
>>> def fEnclosing(): 
...     x = 10 
...     print(x + n)
...     def fNested():
...         y = 5
...         print(y + n)
...     fNested()
...         
... 
>>> print(n + x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined 

Python Jumpstart Course

Learn the basics of Python, including OOP.

with lots of exercises, easy to follow

The course is available on Udemy.

Overlapping Names

OK, and now let’s see what happens if we have overlapping names. Here’s an example:

x = 5  # x defined in global scope

def func():
    x = 2     # x defined in local scope
    print(x)

func()
print(x)

Here we have the variable x in global scope, to which we assign the value 5. Then, in local scope we assign 2 to x. If we use a variable with the same name as another variable in a function and assign a value to it, we’re not changing the global variable, but instead we’re creating a new local variable. From now on only the local variable is accessible in the function.

But in global scope we still have our global x variable. These to x variables are now referencing different objects.

The output is:

2
5      

And now let’s have a look at the following example:

x = 5

def func():
    x = 2    
    def fNested():
        print(x)
    fNested()

func()  

We know that inside the nested function we have access to variables in local scope, nonlocal scope and global scope. So, if we use the x variable in the nested function without assigning a new value to it, we’re not creating a local x variable there. Now the question is: which one will be used inside the nested function? The one from nonlocal scope or the one from global scope? Let’s check it out.

The output is:

2

Turns out the nonlocal scope wins!   This is because the order in which the scopes are searched is:

local -> nonlocal -> global -> built-in

Blender Jumpstart Course

Learn the basics of 3D modeling in Blender.

step-by-step, easy to follow, visually rich

The course is available on Udemy and on Skillshare.

Built-in Scope

Finally, let’s have a look at the built-in scope. All the built-in objects, like functions, exceptions, and so on are defined in this scope and this scope is used as the last resort if a variable is not found in the local, nonlocal or global scope. We can use the objects defined in built-in scope out of the box. But what if we define a variable with the same name in another scope? Let’s see.

In the built-in scope we have the function len, which is used to return the length of a sequence. Now let’s define our own len function in global scope:

>>> def len(x):
...     return "*** " * x
... 
>>> len(5)

And here’s the output:

'*** *** *** *** *** '

As soon as len is defined in global scope, it is a new global object and from now on it will be referenced instead of the built-in function whenever it’s used.  This is because when we now use the len function, it’s first looked up in the local scope. If it’s not found there, the search moves on to nonlocal and then to global scope and there it is found. The search is over and the built-in scope is not looked up at all.

Well, redefining objects from the built-in scope is a good way to break the references to built-in objects, so use it sparingly if you really have to.

Now, is there a way of accessing the global variable from inside a function? Actually there is, but this is a topic for another article.

Here’s the video version:


Spread the love

Leave a Reply