Monday, February 8, 2021

Python Inner Functions: What Are They Good For?

Inner functions, also known as nested functions, are functions that you define inside other functions. In Python, this kind of function has direct access to variables and names defined in the enclosing function. Inner functions have many uses, most notably as closure factories and decorator functions.

In this tutorial, you’ll learn how to:

  • Provide encapsulation and hide your functions from external access
  • Write helper functions to facilitate code reuse
  • Create closure factory functions that retain state between calls
  • Code decorator functions to add behavior to existing functions

Creating Python Inner Functions

A function defined inside another function is known as an inner function or a nested function. In Python, this kind of function can access names in the enclosing function. Here’s an example of how to create an inner function in Python:

>>>
>>> def outer_func():
...     def inner_func():
...         print("Hello, World!")
...     inner_func()
...

>>> outer_func()
Hello, World!

In this code, you define inner_func() inside outer_func() to print the Hello, World! message to the screen. To do that, you call inner_func() on the last line of outer_func(). This is the quickest way to write an inner function in Python. However, inner functions provide a lot of interesting possibilities beyond what you see in this example.

The core feature of inner functions is their ability to access variables and objects from their enclosing function even after this function has returned. The enclosing function provides a namespace that is accessible to the inner function:

>>>
>>> def outer_func(who):
...     def inner_func():
...         print(f"Hello, {who}")
...     inner_func()
...

>>> outer_func("World!")
Hello, World!

Now you can pass a string as an argument to outer_func(), and inner_func() will access that argument through the name who. This name, however, is defined in the local scope of outer_func(). The names that you define in the local scope of an outer function are known as nonlocal names. They are nonlocal from the inner_func() point of view.

Here’s an example of how to create and use a more elaborate inner function:

>>>
>>> def factorial(number):
...     # Validate input
...     if not isinstance(number, int):
...         raise TypeError("Sorry. 'number' must be an integer.")
...     if number < 0:
...         raise ValueError("Sorry. 'number' must be zero or positive.")
...     # Calculate the factorial of number
...     def inner_factorial(number):
...         if number <= 1:
...             return 1
...         return number * inner_factorial(number - 1)
...     return inner_factorial(number)
...

>>> factorial(4)
24

In factorial(), you first validate the input data to make sure that your user is providing an integer that is equal to or greater than zero. Then you define a recursive inner function called inner_factorial() that performs the factorial calculation and returns the result. The final step is to call inner_factorial().

The main advantage of using this pattern is that, by performing all the argument checking in the outer function, you can safely skip error checking in the inner function and focus on the computation at hand.

Using Inner Functions: The Basics

The use cases of Python inner functions are varied. You can use them to provide encapsulation and hide your functions from external access, you can write helper inner functions, and you can also create closures and decorators. In this section, you’ll learn about the former two use cases of inner functions, and in later sections, you’ll learn how to create closure factory functions and decorators.

Providing Encapsulation

A common use case of inner functions arises when you need to protect, or hide, a given function from everything happening outside of it so that the function is totally hidden from the global scope. This kind of behavior is commonly known as encapsulation.

Here’s an example that highlights that concept:

>>>
>>> def increment(number):
...     def inner_increment():
...         return number + 1
...     return inner_increment()
...

>>> increment(10)
11

>>> # Call inner_increment()
>>> inner_increment()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    inner_increment()
NameError: name 'inner_increment' is not defined

In this example, you can’t access inner_increment() directly. If you try to do it, then you get a NameError. That’s because increment() totally hides inner_increment(), preventing you from accessing it from the global scope.

Read the full article at https://realpython.com/inner-functions-what-are-they-good-for/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]



from Real Python
read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...