DEV Community

Main
Main

Posted on • Originally published at pynerds.com on

Python Function Scope

When you use a name in a Python program such as variable name function name,etc, Python creates, changes or looks up the name in a namespace. A namespace is the complete list of names that exists in a given context.

There are two types of namespaces, global namespace and local namespace.

The scope of an object determines the locations in the program where it can be accessed, by default, objects are only accessible from within the namespace that they occur. Objects in the global namespace are accessible from anywhere in the program, this are the names that are declared at the top level of a module or a script i.e not inside a function, a class, etc. On the other hand, names declared inside a block such as a function are local to that block and are only accessible within the block.

When we define a function, Python sets up a local namespace for the function. Any object declared inside the function will only be accessible within the function, the object is said to be local to that function. Trying to access local objects outside their scope will raise a NameError. For example:

def demo():
    x = 100
    print(x)

demo()
//100
print(x)
//NameError: name 'x' is not defined

Enter fullscreen mode Exit fullscreen mode

Objects inside a function are localized such that they will not crash with objects with similar names outside that function.This allows same name to be used for different objects in different scopes without causing conflicts.

If a function declares a name that also exists in the global namespace, the local name takes precedence over the global name and overwrites it only inside that function. Example:


x = 200
def demo():
    x = 100
    print(x)

demo()
//100
print(x)
//200

Enter fullscreen mode Exit fullscreen mode

In the above example, there are two distinct variables with a name x, one in the global namespace with a value of 200and another in function demo's local namespace with a value of 100. The value of xis, therefore, determined by the location where we are using it. Making any changes to the'x' inside the function demo does not affect in any way the value of the 'x' outside its scope.

Name Resolution, The LEGB Rule

When we mention an object by name, the Python interpreter follows an inside-out approach in order to identify the object we are referring to. This approach is often referred to as the LEGB rule, which stands for Local, Enclosing, Global, Built-in. This rule can be summarized as follows:

  1. Local, first : Look for this name first in the local namespace and use local version if available. If not, go to higher namespace.
  2. Enclosing, second : If the current function is enclosed within another function, look for the name in that outer function. If not, go to higher namespace.
  3. Global, third : Look for the name in the objects defined at global namespace
  4. Built-in,last : Finally, look for the variable among Python’s built-in names.

LEGB rule

If the interpreter goes through all the 4 stages and does not locate the name, a NameErroris raised, of course if the operation was not an assignment operation in which case an object with that name will be created in the local scope instead of raising the error.

Example:


x = 200
def demo():
    x = 300
    def inner():
        print(x)
    inner()

demo()
//300

Enter fullscreen mode Exit fullscreen mode

In the above case, in the call to printx, the interpreter fails to find an object with a name x in the function inner's scope, it moves outward to the enclosing function in which case it finds an'x' with a value of 300thus terminating the search. If still an object with a name 'x'was not located in the enclosing function 'demo', the globalxwith a value of 200would have been returned.

More Examples:


x = 500
def demo():
   print(x)
   x = 300
   print(x)

demo()
//500
//300
def demo2():
   y = 400
   def inner():
       print(x + y)
   inner()

demo2()
//900

Enter fullscreen mode Exit fullscreen mode

The Global Statement

The global statement is the only thing that is capable of transforming a name defined inside a function to a global name. This means that the name will behave just like names defined in the global namespace and will be accessible from anywhere in the program.

Examples:


def demo():
   global x
   x = 200

demo()
print(x)
//200

Enter fullscreen mode Exit fullscreen mode

If a global name exists similar to the name specified in the global statement, any modifications done to the object it identifies will take effect at a global level. Examples:


x = 100
def demo():
    global x
    x = 500

demo()
print(x)
//500

Enter fullscreen mode Exit fullscreen mode

To declare multiple global names in one global statement, we use the global keyword followed by the comma separated names, example:


def demo():
   global x, y, z
   x = 4
   y = 3
   z = x + y

demo()
print(x, y, z)
//4, 3, 7

Enter fullscreen mode Exit fullscreen mode

While the global statement can be useful at times, it can make code mor obscured as it can make it difficult to keep track of the global names. It should, therefore, be avoided whenever possible, after all names inside a function are made local to that function by default because it is the best policy. We can Instead, pass global values as function arguments and then return the modified values.

The nonlocal Statement

The nonlocalstatement is a close cousin of the globalstatement. While the globalstatement makes a name available at global level, the nonlocalstatement makes a name available to the enclosing function's scope. This also allows the nested function to make changes to variables defined in the enclosing function. Examples:


def demo():
    x = 400
    def inner():
        nonlocal x
        x = 600
    inner()
    print(x)

demo()
//600
def demo2():
    def inner():
        nonlocal y 
        y = 500
    inner()
    print(y)

demo2()
//500

Enter fullscreen mode Exit fullscreen mode

Note: We cannot use the nonlocalstatement with a top level function, trying this will raise a SyntaxError


def demo():
    nonlocal y
    y = 100

demo()
//SyntaxError: no binding for nonlocal 'y' found

Enter fullscreen mode Exit fullscreen mode

Related articles


Top comments (0)