The Truth About Python Decorators You Should Know

The Truth About Python Decorators You Should Know

In general, a decorator is a software pattern used to alter the operation of a certain piece of code, either a function or a class, without using other mechanisms such as inheritance.

Specifically, when talking about python decorators, we refer to functions or objects with similar behavior that allow us to alter how other entities work without modifying their code explicitly.

To understand the idea a little better, you need to clarify a few things about functions. Functions are, for practical purposes, objects, and they can be passed as parameters, reassigned, returned by other functions and even defined within functions; that is, they can be nested.

Take the following code as an example:

def pairs (numbers):

     def is_pair (number):

         return (number% 2) == 0

     return list (filter (is_pair, numbers))

Here the nested function is_pair ()is used to filter the even numbers in an arbitrary list of values.

In the case of decorators, the nested functions are used to perform tasks around the execution of the routine that we want to decorate. To illustrate the situation, I will show you a decorator who takes a text that consists of several lines and each of these prefixes the corresponding line number. Let’s start with an ordinary function that performs this task:

def add_line_number (text):

     lines = []

     for number, line in enumerate (text. split (‘\ n’), 1):

         lines. append (f ‘{number: 6} {line}’)

     return ‘\ n’. join (lines)

Thus, if we wanted to have numbered lines in a text, just call this function:

def dictionary_to_text (dictionary):

     text = ”

     for key, value in dictionary.items ():

         text + = f ‘{key: 12}: {value} \ n’

     return text

Faruque = {

     ‘name’: ‘Faruque Azam’,

     ‘age’: 35

}

print (dictionary_to_text (Faruque))

name: Faruque Azam

age: 35

print ( add_line_number ( dictionary_to_text ( faruque ) ) )

1  name: Faruque Azam

2  age: 35

3

However, this doesn’t look very fancy, and reusing the add_line_number () function isn’t that straightforward. A decorator will help us hide the code that numbers the line and take advantage of it in other functions in an easy and elegant way:

def with numbered_line ( function ) :

    def add_line_number ( * args, ** kwargs ) :

        result = function ( * args, ** kwargs )

        lines = [ ]

        for number, line in enumerate ( str ( result ) . split ( ‘\ n’ ) , 1 ) :

            lines. append ( f ‘{number: 6} {line}’ )

        return ‘\ n’ . join ( lines )

    return add_line_number

The above function is a decorator, which takes as an argument another one defined outside, and returns a third internally defined routine, a nested function. The interesting part is that the nested function invokes the external one and later processes said result, adding the functionality of the decorator to the final result. Once the decorator is defined, we can use it as follows:

def dictionary_to_text ( dictionary ) :

    text = ”

    for key, value in dictionary. items ( ) :

        text + = f ‘{key: 12}: {value} \ n’

    return text

dictionary_to_text = with_line_numbered ( dictionary_to_text )

Faruque = {

    ‘name’ : ‘Faruque Azam’ ,

    ‘age’ : 35

}

print ( dictionary_to_text ( faruque ) )

1  name: faruque Azam

2  age: 35

3

Note, as immediately after defining the dictionary_to_text function, it is redefined using the decorator. This instruction is so common when using decorators, that Python has defined a bit of syntactic sugar to represent it: simply place the name of the decorator, with an at the prefix, before the definition of the function you want to decorate, in our case, the definition will look like this:

@number_line

def dictionary_to_text ( dictionary ) :

    text = ”

    for key, value in dictionary. items ( ) :

        text + = f ‘{key: 12}: {value} \ n’

    return text

Faruque = {

    ‘name’ : ‘Faruque Azam’ ,

    ‘age’ : 35

}

print ( dictionary_to_text ( faruque ) )

1  name: Faruque Azam

2  age: 35

3

This makes reusing the decorator even easier:

@number_line

def sample_file ( file_name ) :

    With open ( filename ) as file: 

        return file. read ( )

As you can see, python decorators are very useful to reuse code that performs common tasks; the possibilities are very diverse, from formatting text, as in the example I have shown you, synchronizing tasks in parallel, or handling errors. The limit is our creativity. I hope this post has served you and that you get the most out of this tool.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like