Nested functions. Nested scopes. Name search rules in case of nested functions. Factory functions. Passing values to a nested function
- 1. What are nested functions?
- 2. Names lookup rules inside a function in case of nested scopes
- 3. Examples of nested functions
- 4. Factory functions. Basic concepts. Example
- 5. Passing data from the enclosing scope to a nested function using default arguments. Example
- Related topics
Search other websites:
1. What are nested functions?
Nested functions can be used in the Python programming language. This means that there can be another def statement inside a def statement. The number of function nesting is arbitrary. In the most general case, the form of declaring nested functions is as follows:
def Fn_1(parameters1): ... def Fn_2(parameters2): ... def Fn_N(parametersN): ... return ... return return
- Fn_1, Fn_2, Fn_N – names of functions that receive parameters1, parameters2, parametersN respectively.
2. Names lookup rules inside a function in case of nested scopes
If a name (variable) is declared inside a function that is nested in another function, then the following rules apply:
1. If a name (variable) is declared inside a function that is nested in another function, then the following rules apply:
- search in the local scope;
- search in all enclosing functions in order from the inside out;
- search in the current global scope (search in the current module);
- search in built-in scope (buildins module).
2. If a global name is declared inside a function using the global statement, then the search for the name starts immediately from the global scope.
3. If the default assignment operation (t = value) is used for some object, then this operation:
- creates a new name in the local scope;
- changes the name in the current local scope.
4. If a global name is defined inside the function (using the global keyword), then the assignment operation:
- creates a new name in the scope of the enclosing module;
- changes the meaning of the name that was declared in the enclosing module.
5. If a nonlocal name is defined inside the function (nonlocal instruction), then the assignment operation:
- creates a new name in the nearest enclosing function;
- changes the name that was created in the nearest enclosing function.
3. Examples of nested functions
Example 1. You are given a SquareEquation() function that calculates solutions to a quadratic equation. Inside the function is a nested Disc() function that calculates the discriminant.
# Nested functions import math # Quadratic equation solution function, # this function is enclosing the nested function Disc(). # The function returns the solution to the equation as a list def SquareEquation(a, b, c): # Nested function Disc() calculating a discriminant def Disc(a, b, c): d = b*b-4*a*c return d # Calculate the discriminant D = Disc(a, b, c) if D>=0: x1 = (-b - math.sqrt(D))/(2*a) x2 = (-b + math.sqrt(D))/(2*a) return [x1, x2] else: return None # Call the function to solve the equation 2*x^2+3*x-5=0 Res = SquareEquation(2, 3, -5) if Res!=None: print('Result = ', Res) else: print('The equation has no roots')
The result of the program:
Result = [-2.5, 1.0]
The example implements the CalcComplex() function, which, based on the specified value n, implements the nested calculation functions:
- sums of complex numbers – the AddComplex() function;
- difference of complex numbers – SubComplex () function.
The CalcComplex() function returns the result as a list.
# Nested functions import math # Function CalcComplex(), which, based on the specified number, # calls the nested function to evaluate the corresponding expression. def CalcComplex(n, a1, b1, a2, b2): if n==1: # Define AddComplex () function - addition of complex numbers def AddComplex(a1, b1, a2, b2): real = a1+a2 # real part of a complex number imag = b1+b2 # imaginary part of a complex number return [real, imag] # Call the AddComplex() function result = AddComplex(a1, b1, a2, b2) # return the result return result if n==2: # Define SubComplex() function def SubComplex(a1, b1, a2, b2): real = a1-a2 # real part imag = b1-b2 # imaginary part return [real, imag] # call the SubComplex() function result = SubComplex(a1, b1, a2, b2) # Return the result return result # Otherwise return None return None # Find the sum of complex numbers: # (2-3*j) + (-2+5*j) Result = CalcComplex(1, 2, -3, -2, 5) # Return the result print("Result = ", Result)
The result of the program
Result = [0, 2]
4. Factory functions. Basic concepts. Example
As you know, when a function is called, an object of this function is created. This object has its own address (reference). For nested functions, you can store the nested function object in an enclosing function. If a nested function object is stored, then such a function is called a factory function. In this case, the requirement is related to the enclosing function: the enclosing function must return a reference to the object of the nested function in the return statement.
After declaring a factory function in a program, it can be used as follows:
- create a factory function object by calling the enclosing function. This creates a reference to the factory function object. In addition, the factory function object stores all values that were passed to it from the enclosing function;
- call the factory function using the generated reference.
In general, the process of creating and using a factory function is as follows.
1. Declare nested functions as shown below
def Fn1(parameters1): ... def Fn2(parameters2): # this is a factory function ... # using parameters1 parameters of the enclosing function # ... ... return value # function Fn2 () returns some result ... return Fn2 # function Fn1() returns a reference to the object of function Fn2()
2. Get a reference to a nested factory function object. To get a reference, you need to call the enclosing function Fn1() with the arguments that are passed to it
ref = Fn1(arguments1)
In this case, arguments1 of the enclosing Fn1() function are converted to parameters1. These parameters are used and stored in the object of the nested function Fn2().
3. Call factory function object Fn2() using ref
here arguments2 is converted to parameters2 of the nested function Fn2().
Thus, the program calls the factory function
Example 1. A function that calculates the factorial of a number n: n! = 1·2·3· … ·n.
# Factory functions. Calculating the factorial of a number # 1. Define an external function Factorial (n), # which just declares and returns a nested function Fact() def Factorial(n): # Declare a nested function for calculating factorial def Fact(): # function Fact() saves the value n # from the enclosing scope and uses it if (n<=0): return 0 i=1 res=1 while i<=n: res=res*i i=i+1 return res # Return the result of a Fact () function without calling it return Fact # 2. Save the reference to the Factorial (5) function, # which calculates 5! ref5 = Factorial(5) # Print the reference print("ref5 = ", ref5) # ref5 = <function Factorial.<locals>.Fact at 0x035176A8> # Call the function for calculating factorial 5! # follow the reference and get the result F5 = ref5() print("5! = ", F5) # 5! = 120 # 3. Get another reference to the function that calculates 8! ref8 = Factorial(8) # Print ref8 and the result of the calculation 8! print('ref8 = ', ref8) print("8! = ", ref8()) # 4. Go straight to the function for calculating factorial 10! print("10! = ", Factorial(10)()) # 5. Call the calculation function 5 again! via ref5 reference, # ref5 is stored print("5! = ", ref5()) # 5! = 120
The result of the program
ref5 <function Factorial.<locals>.Fact at 0x032E2150> 5! = 120 ref8 = <function Factorial.<locals>.Fact at 0x033376A8> 8! = 40=320 10! = 3628800 5! = 120
Example 2. Using a factory function that takes parameters. The example declares functions that calculate the n-th root of the number x. The enclosing function receives the value n, which is saved in the factory function. The factory function, in turn, receives an x parameter.
# Nested functions. Factory functions # Getting the n-th root of x # 1. Declare an enclosing Root() function with a nested Rt() function def Root(n): # the root of the n-th degree # Declare a nested function that receives the number x def Rt(x): # the nested function saves the parameter n of the enclosing function return x**(1.0/n) # Return a reference to the nested function return Rt # 2. Save reference to enclosing function with parameter 3 ref = Root(3) # ref = <function Root.<locals>.Rt at 0x0032B588> print('ref = ', ref) # 3. Calculate the root of the third degree of the number 27 result = ref(27) # calls Root(3)(27) print('27(1/3) = ', result)
The result of the program
ref = <function Root.<locals>.Rt at 0x030E2150> 27^(1/3) = 3.0
5. Passing data from the enclosing scope to a nested function using default arguments. Example
From an enclosing function, you can pass data to a nested function using default arguments. The general form is as follows
def FnOut: varOut = value def FnIn(varIn = varOut): # varOut - default argument # using varIn ... return ... return
- FnOut – enclosing function;
- FnIn – nested function;
- varOut – variable in the enclosing function that is passed to the inner function;
- value – the value that varOut receives in the enclosing function;
- varIn – a variable in a nested function that is passed the value of the enclosing function’s varOut variable. The value is passed as a string varIn = varOut.
The example implements the calculation of the area of a triangle using Heron’s formula. The nested Semiperimeter() function is passed three default arguments from the AreaTriangle() function.
# Nested functions # Include math module import math # Determine the AreaTriangle() function # which calculates the area of a triangle according to Heron's formula # by the lengths of its sides a, b, c. def AreaTriangle(a, b, c): # Checking whether it is possible to form a triangle from the sides a, b, c if (((a+b)<c)or((b+c)<a)or((a+c)<b)): return 0 # The AreaTriangle() function determines a nested function Semiperimeter() # that receives the parameters a, b, from the enclosing function # and, based on these parameters, calculates the semiperimeter. # Parameters a, b, c are obtained as default arguments def SemiPerimeter(a=a, b=b, c=c): return (a+b+c)/2 # Calculate the semiperimeter p = SemiPerimeter(a, b, c) # Calculate the area area = math.sqrt(p*(p-a)*(p-b)*(p-c)) return area # Demonstrate the AreaTriangle() function Area = AreaTriangle(5, 7, 3) print('Area = ', Area) Area = AreaTriangle(1, 1, 5) print('Area = ', Area)
The result of the program
Area = 6.49519052838329 Area = 0
- Concept of function. General form. Examples of declaring and using functions
- Scopes of names. Local and global names. Visibility rules for names. LEGB rule. The global keyword. Overriding names in function