Python decorators basics. Make functions prettier. Or not?

Carlos Cárdenas
4 min readJan 6, 2021
Photo by Nataliya Vaitkevich from Pexels

Hi there. Wonder what decorators are and why you might need’em. Then look no more, let me try to explain in simple words what is it and how to use it practically.

What is a decorator?

I have always wondered why the term “decorator” is used to describe, well, a decorator. It doesn’t make the functions any prettier, sometimes the opposite, but hey, let’s leave that discussion to the semantic experts.

Well, a decorator is just changing the inner works of a function at runtime, that’s using program functionality as data.

Wait a minute… that’s the definition of metaprogramming

And you are right, a decorator is a way of having our program, modify one part of itself at runtime. Yes, we are doing pretty advanced stuff.

Neither do I…

Before going deeper into decorators we need to comfortable with the idea, that everything in python is an object, this includes classes and function. So as an object, we can use them as parameters or even assign them to variables. it sounds confusing, it is sometimes, but hey… I think a bit of code can make it easier to digest.

def function_one(arg1):
return f"{arg1}"
# prints "Sample1"
print(function_one('Sample1'))
# function_two now "points" to the same function_one object
function_two = function_one
# prints "Sample2"
print(function_two('Sample2'))

Yes, you can assign a function to a variable, but what about passing it as an argument. Let’s define a couple of function, that takes an argument and multiply it by 2 and 3 respectively, and then a third function is used to call this function using parameters.

def multiply_by_two(arg1):
return arg1*2
def multiply_by_three(arg1):
return arg1*3
def process_num(funct, num):
return funct(num)
print(process_num(multiply_by_two, 2)) # prints 4
print(process_num(multiply_by_three, 3)) # prints 9

And you know something even weirder and by weirder I mean awesome, you can even return functions and save those into variables. :O

def first_function():
def second_function():
print("Second layer called")
return second_function
new = first_function()
# prints "Second layer called"
new()
It’s magic

So now that we have crystal clear that functions are objects and can be passed as parameters, saved to variables, and even returned, let’s get into the decorators.

Decorators

A decorator explained in plain English, basically takes a function as a parameter, adds or changes functionality, and returns this “new function”.

Let’s get into a simple example, we have a function that returns the sum of two numbers, and we want to change it so it returns the difference instead. Let’s see what happens under the hood.

def change_to_diff(func):
def inner(a, b):
return a - b
return inner
def sum(a, b):
return a + b
print("Call the regular function")
print(sum(2, 3)) # Regular function called
sum = change_to_diff(sum(2, 3))
print("Call the decorated function")
print(sum(2, 3)) # Decorated function called

As you can see using an assignment we called the function using the same parameters and name, but we changed the function, the above code can, or cannot be considered a decorator, depends on the point of view, but the concept is the same.

Now let’s do the same thing, but using the python decorator syntax.

def change_to_diff(func):
def inner(a, b):
return a - b
return inner
@change_to_diff
def sum(a, b):
return a + b
print(sum(2, 3))

What happens? Well, it is the same result as above, but with less code, so we can consider that:

@change_to_diff
def sum(a, b):
return a + b
print(sum(2, 3))

Is the same as:

def sum(a, b):
return a + b
sum = change_to_diff(sum(2, 3))
print(sum(2, 3)) # Called decorated

We are just using the decorator syntax, we just use the ‘@’ symbol, followed by the decorator function name. It is easier to read and fewer keystrokes mean a larger life span for our beloved keyboard.

By the way, Did you notice that the arguments are being passed to the internal (inner) function again? If you didn’t, take a look at the code again. This means we can also work with args and kwargs. No that’s not Klingon, that’s just the way python handles an unknown number of parameters.

If you don’t know what args and kwargs are, don’t worry, I may be covering that in the future. Maybe.

I hope you liked this short explanation about one of the more powerful, dangerous, and unused design patterns in Python.

Photo by Andrea Piacquadio from Pexels

What? A Design pattern?
Yes, a decorator is one of the multiple design patterns someone has documented in the past for you to use, and reuse whenever you need it.
But, please, don’t abuse…

Have fun and happy coding.

--

--

Carlos Cárdenas

VP Of Engineering at Bimbau, team player, I believe in blockchain and Machine Learning. Building great products!!!