A Dive Into Python Closures and Decorators – Part 1
This is a multi-part guest tutorial post by Moyosore Sosan, an Andela developer based in Lagos.
We will be looking at local functions, the concepts of closure and what decorators are, sounds like a lot? Then let’s dive into it.
Probably most functions you are familiar with are either defined in module/global scope or within classes i.e methods. However Python allows us to define our functions in a local scope i.e within a function.
Knowing this, local functions are functions defined within another function. We say these functions are defined to the scope of a specific function e.g
From the above, we see that calling get_first_item throws an error. This is because it can only be accessed within the remove_first_item function, that makes it a local function.
Local functions can also be returned from functions. Returning a local function is similar to returning in any other object. Let’s take a look:
Local functions are subject to the same scoping rules as other functions, this brings to the
LEGB rule for name look up in python – checking starts with the local scope, then the enclosing, then global and finally the built-in scope
LEGB – Local, Eclosing, Global, Built-in
Local functions can be used when there is need for specialized functions. They also help with code organization and readability.
The local functions we have looked at so far have no definite way of interacting with the enclosing scope, that is about to change. Local functions can make use of variables in their enclosing scope, the
LEGB rule makes this possible.
From the above, we see that the inner_func function makes reference to the outer_func fucntion for the value of x. The local function is able to reference the outer scope through
closures. Closures maintain references to objects from the earlier scope.
Closure is commonly used in what is referred to as Function Factory – these are functions that return other functions. The returned functions are specialized. The Function Factory takes in argument(s), creates local function that creates its own argument(s) and also uses the argument(s) passed to the function factory. This is possible with closures
Here we see that the local function multiply_by_num takes an argument
k and returns the argument multiplied by the argument of it’s enclosing function. Also, multply_by fuction takes an argument
num and returns the function that multiplies
num by its argument.
LEGB rule does not apply when new name bindings happen
In the inner_func function, we have created a new name binding by re-assiging the variable
text in the function scope. Calling the inner_func does not affect the variable
text in the outer_func, likewise calling outer_func does not affect the global variable
global is a python keyword that introduces names from global namespace into the local namespace. From the previous code, we can make the inner_func to modify the variable
text rather than create a new one.
global keyword has binded the global
text variable to the local
textvariabel, calling the outer_func function makes changes to the global text, hence, reassigning the variable
text in the global scope.
nonlocal keyword allows us to introduce names from enclosing namespace into the local namespace. Still looking at the previous code:
Here, the enclosing
text variable changes when the inner_func was called.
Since we have understanding of local functions and closure, we can then look at function decorators.
Decorators are used to enhance existing functions without changing their definition. A decorator is itself a callable and takes in another callable to return another callable. Simply put – A decorator is a function that takes in another function and returns another function, although it is a bit more than just that.
The result of calling my_function is passed into the my_decorator
The result of calling say_hello is passed into the capitalize decorator. The decorator modifies the say_hello function by changing its result to uppercase. We see that capitalize decorator takes in a callable(say_hello) as an argument and returns another callable(uppercase). This is just a basic example on decorator
In the next post, we will continue with understanding how decorators work in python. In preparation for that, let’s take a brief look at
*args and **kwargs
*args and **kwargs
*args allows you to use any number of arguments in a function. You use it when you’re not sure of how many arguments might be passed into a function
**kwargs can take many arguments you would like to supply to it. However,
**kwargs differs from
*args in that you will need to assign keywords
Args and kwargs can be used together in a function, with args always coming before kwargs. If there are any other required arguments, they come before args and kwargs
You can do more reading on args and kwargs for better understanding. This is just an example for you to have basic idea on args and kwargs.
Next post will be dealing with decorators in details!