Python. The nonlocal keyword. Features of use. Examples




The nonlocal keyword. Features of use. Examples

Before using this theme are encouraged to review the following theme:


Contents


Search other websites:

1. What is the nonlocal instruction for? General form of use

 The nonlocal instruction is used when nested functions are implemented in the program. The nonlocal instruction is intended to change the value of an enclosing function variable in the body of a nested function. Thus, the nested function has access to the variables of the enclosing functions.

Using this mechanism makes it possible to maintain modifiable information about the state, which is restored on subsequent calls to the nested function.

The general form of the use of nonlocal statement

def Fn():
    nonlocal name1, name2, ..., nameN

here

  • Fn() – a function that is nested in some other function;
  • name1, name2, nameN – names that exist in the scope of the enclosing function. The Fn() function is nested in another enclosing function.

In the case of a simple nesting of the FnIn() function within the enclosing FnOut() function, the general form of declaring a nonlocal instruction is as follows:

def FnOut():
    ...
    def FnIn():
        nonlocal name1, name2, ..., nameN
    ...

here

  • FnOut() – an enclosing function in which a nested function FnIn() is declared;
  • FnIn() – a nested function that contains a nonlocal statement;
  • name1, name2, nameN – names that are declared in the enclosing FnOut() function and used in the nested FnIn() function.

 

2. Features of using the nonlocal instruction. Difference between global and nonlocal statements

The nonlocal instruction is used in nested functions when you need to change variables that have been declared in enclosing functions.

The difference between nonlocal and global statements is as follows:

  • the global statement allows you to change the values of variables in the global scope of the module;
  • the nonlocal statement changes the value of variables in the enclosing function. The nonlocal statement has no effect for the global scope.

 

3. Peculiarities of using the nonlocal statement in the case when several levels of nested functions are used. Example

Example. Let several nested functions be given. The outer function Fn1() contains the embedded function Fn2(). The nested function Fn2() contains another nested function Fn3(). The functions Fn1() and Fn2() declare a variable named x1, which is assigned some value. In the Fn3() function, the variable x1 is modified using the nonlocal instruction. As a result, the variable x1 of the nearest enclosing function will change, in our case – the function Fn2().

# Instruction nonlocal

# Several enclosing functions

def Fn1():
    # this variable does not change from the function Fn3(),
    # because it is overridden by the variable from the function Fn2 ()
    x1 = 25

    def Fn2():
        x1 = 33 # this variable will be changed in the function Fn3()

        def Fn3():
            nonlocal x1
            x1 = 55 # Fn2.x1 = 55

        Fn3()
        print('Fn2.x1 = ', x1)

    Fn2()
    print('Fn1.x1 = ', x1)

Fn1()

The result of the program

Fn2.x1 = 55
Fn1.x1 = 25

If we remove the line x1 = 33 in the body of the function Fn2(), then the value of the variable x1 of the higher-level function, that is, the function Fn1(), will change. In this case, the program will return the result

Fn2.x1 = 55
Fn1.x1 = 55

 

4. Requirements for enclosing functions that contain nested functions with nonlocal statements

In order to use the nonlocal statement in a nested function, the following conditions must be met:

  • the program contained code of nested functions. One of the functions is considered as enclosing, the other is nested;
  • a variable was declared in the enclosing function, which is changed in the nested function. This variable must be declared before the nested function declaration.

 

5. Differences in the rules for finding names by the interpreter in the global and nonlocal statements

If a nonlocal instruction is used in a nested function, the interpreter does the following:

  • skips the local scope of the function in which the nonlocal statement is declared;
  • searches the scopes of enclosing functions starting with the nearest enclosing function.

When the global statement is used in a nested function, the interpreter searches the enclosing module’s namespace (global scope).

 

6. Examples of using the nonlocal instruction

Example 1. The example demonstrates access to the out variable of the enclosing FnOut() function from the FnIn() function.

# The nonlocal statement

# Enclosing function FnOut()
def FnOut():
    out = 25 # declare out variable

    # The nested function which uses nonlocal statement
    def FnIn():
        # access to the out variable of the enclosing function FnOut()
        nonlocal out
        out = 777;
        return

    # Calling the nested function FnIn() from the body of the function FnOut()
    FnIn()

    # Print the out variable
    print('FnOut.out = ', out)

# Global scope.
# Invoke function FnOut()
FnOut() # FnOut.out = 777

The result of the program

FnOut.out = 777

As you can see from the result, the out variable from the FnOut() function was changed in the body of the FnIn() function due to the nonlocal instruction.

Example 2. The solution of the quadratic equation. The example demonstrates access to the variables x1, x2 of the enclosing SquareEquition() function from the nested Calc() function.

# The nonlocal statement. The solution of quadratic equation

# Include a math module in order to use the math.sqrt() function
import math

# Declare two functions: enclosing SquareEquition() and nested Calc()
def SquareEquition(a, b, c):

    # Declare two functions: enclosing SquareEquition() and nested Calc()
    x1 = None
    x2 = None

    # Nested Calc() function - fills in values x1, x2
    def Calc(a, b, c):
        D = b*b-4*a*c

        if (D>=0):
            # access to x1, x2 enclosing SquareEquition() function
            nonlocal x1, x2 # declare 2 names in the nonlocal instruction
            x1 = (-b - math.sqrt(D))/(2.0*a)
            x2 = (-b + math.sqrt(D))/(2.0*a)

    Calc(a, b, c)
    return [x1, x2]

# Using the function SquareEquition(),
# input data
print("Input a, b, c:")
a = float(input('a = '))
b = float(input('b = '))
c = float(input('c = '))
roots = SquareEquition(a, b, c)
print('roots = ', roots)

The result of the program

Input a, b, c:
a = 1
b = 1
c = -2
roots = [-2.0, 1.0]

 

7. What restrictions are imposed on nonlocal statement?

The following restrictions apply to the nonlocal instruction:

  • the name that is declared in the nonlocal statement must already exist in the enclosing function at the time of the call in the nested function;
  • when using the statement, names are looked up only in the scope of enclosing functions. The built-in and global scopes are not searched for nonlocal names, even if such names already exist in those scopes (see example below).

Example. If you try the following example

# Restrictions that are imposed to nonlocal statement

D = 'Hello' # Declare a global name

def FnOut():

    def FnIn():
        # Trying to bind name D to global name D
        nonlocal D
        D = 'world!'

    # Invoke function FnIn() from the function FnOut()
    FnIn()
    print('FnIn.D = ', D)

# Call FnOut() function from module scope
FnOut()

the interpreter will throw an error:

SyntaxError: no binding for nonlocal 'D' found

This means that the name D must be previously declared in the enclosing FnOut() function, and not in the global scope. For example, the FnOut() function can be changed as follows

...

def FnOut():

    D = 'Hello'

    def FnIn():
        # the name D binds with the name D of the enclosing function
        nonlocal D
        D = 'world!'

    # Invoke the FnIn() function from the body of the FnOut() function
    FnIn()
    print('FnIn.D = ', D)

...

After that, the program will work and give the result

FnIn.D = world!

 

8. An example where multiple names are declared in a nonlocal statement

 

# Addition of complex numbers (a1+b1*j)+(a1+b2*j)
def ComplexAdd(a1, b1, a2, b2):
    a3 = 0
    b3 = 0

    def Add():
        nonlocal a3, b3 # Using two names in the nonlocal statement
        a3 = a1+a2
        b3 = b1+b2

    Add()
    return [a3, b3]

result = ComplexAdd(2, 3, -1, 4)
print('result = ', result) # result =   [1, 7]

 


Related topics