An Introduction to Decorators in Python
A beginner’s guide to understanding decorators in Python.
World of Objects
Have you ever wondered — Why a simple data type as an integer which is a byte in other programming languages takes up more memory in Python?
In Python, integers are objects. That means it has an attribute and a function associated with it. If you stay in Python long enough, you are bound to hear- everything in Python is an object. Everything you can think of like data types, classes function, exceptions, are first-class citizens and behave like any other object. It means they can be passed as an argument to another function, be returned as the output of the function, and stored as attributes for any other objects. If you don’t believe me try experimenting in Python.
This simple design allows us to do metaprogramming
- it is a powerful feature that can be used to modify the program. Metaprogramming sounds big and scary cause it is. They are hard to understand, complex to use, and easy to mess up. Luckily, most of us will never need to use it in our normal programming journey. , However, in Python, we have a simple feature called a decorator to carry out our metaprogramming.
Decorators
Python community loves decorators. Rightfully so, it takes abstraction to a new level while maintaining the readability of code. Decorators in layman’s terms allow us to modify the functions without having to touch the code written inside them.
In the above example, we have a function fibonacci
which takes a number returns an output. We have a decorator which takes a function as an argument and variance arguments. It prints function details and calculates the time taken to execute the function. To get the decorator we can use decorator(fibonacci, 10)
.
Once we run the above code we get:
fibonacci(10,)->3628800 total_time: 3.337860107421875e-06
The above way is how we use modules like timeit
, Thread
and Processs
.
Syntactic sugar
Now, we will look at the more popular way of creating a decorator using the @
keyword.
In the above snippet, we have created a wrapper that will enclose our function and add extra print to the functions. The decorator will return the wrapper. To add the wrapper for fibonacci()
function we simply put @decorator
. Now we can use the fibonacci()
function as a normal method and it will run the wrapper. This is the way we use popular tools like lru_cache
.
Passing Arguments to Decorator
Passing an argument to the decorator requires one more wrapper.
Here we created the top module with default_arg. Once the decorator is called it will create another wrapper that will wrap the function. This is what we use in a library like flask
and fast api
.
Generic Template
This is the generic syntax for decorators. The *args
we have seen in the above examples are a list of arguments. While **kwargs
are a list of keyword arguments. Together they can handle any argument given to use:
The output of the above function is:
args: (1, 2, 3) <class ‘tuple’> kwargs: {‘a’: ‘a’, ‘z’: ‘b’} <class ‘dict’>
Conclusion
Decorators are a deadly arsenal to your coding repertoire. Decorators are one of the most complex core concepts in Python. They may be a little intimidating to approach as beginners. But in learning, we understand more about Python and better practices.
And there you have it. Thank you for reading.
More content at plainenglish.io