In Part I we talked about three technical interview questions I encountered as a data scientist.
What is the difference between*args
and*kwargs
?- What is a decorator?
- What is
iterrows()
used for?
We answered the first question, now let’s talk about what a decorator is (we can use function and class interchangeably). According to ChatGPT:
A decorator allows you to modify the behavior of a function or a class. It is a higher-order function that takes another function as an argument and returns a modified function. Decorators call higher-order functions, which is a function that takes one or more functions as arguments or returns one or more functions.
The primary use of decorators is to extend or alter the behavior of the decorated function or class without permanently modifying it. Decorators can be applied to any callable in Python, meaning they can decorate functions (think of it as a standalone method that’s not in a class), methods (a function defined in a class), or classes (consists of objects and methods).
In the code below, we define a decorator function called @add_buns
that adds prints the top and bottom buns for our veggie patty. We pass in myfunc
, which is the function we want to add the buns to. Next we define the wrapper
function to sandwich the myfunc
in between two buns. The Python convention is to call this function wrapper
so as to know we are ‘wrapping’ the decorator (did you get the gift analogy?) around our function. We can now decorate as many functions as we want. I’m vegetarian but let’s start with a meat burger for all you carnivores, let’s add buns to make_meat_burger
and then I’ll decorate my own make_veggie_burger
. Which burger would you like?
We just demonstrated the main purpose of the decorator, so that we can do the same thing, add_buns
to as many burger functions as we want! This reduces repetitive coding.
#define decorator function
def add_buns(myfunc):
def wrapper():
print("---Top Bun---")
myfunc()
print("--Bottom Bun--")
return wrapper
#decorate our meat burger
@add_buns
def make_meat_burger():
print("Meat Patty")
#decorate my veggie burger
@add_buns
def make_veggie_burger():
print("Veggie Patty")
#call the two functions
make_meat_burger()
make_veggie_burger()
### output ###
---Top Bun---
Meat Patty
--Bottom Bun--
---Top Bun---
Veggie Patty
--Bottom Bun--
Finally, decorators are known as ‘syntactic sugar’, which means making code prettier without changing its functionality. There is actually a more traditional syntax which involves passing the new function into add_buns
as an argument. Suppose instead of a veggie burger we want to add buns to a potato patty, let’s call our new function make_potato_burger
. However, instead of putting @add_buns
on top of this function, we simply define it and pass that function into add_buns. We don’t have to change anything about add_buns
itself, just how we apply it. Recall that in the original definition of add_buns
we returned the wrapper
function, which is why when we assign add_buns_to_potato_burger
to the results of the decorator function it also becomes a function. In general, assigning an object to a function does not make it a function unless you explicitly return a function.
def make_potato_burger():
print("Potato Patty")
add_buns_to_potato_burger = add_buns(make_potato_burger)
add_buns_to_potato_burger()
### output ###
---Top Bun---
Potato Patty
--Bottom Bun--
There is special method in Python that uses the decorator @
symbol but is conceptually different from a decorator, the bult-in @staticmethod
. Unlike regular (instance) methods, static methods do not receive self
as an argument, so they are independent of (and cannot alter) the instance of a class. Static methods are useful for math functions such as sine or cosine because their behavior is the same from instance to instance of a class (hence the name static). Another special method that uses the decorator symbol is @classmethod
that receives the class itself rather than an instance of a class, so it passes in cls
instead of self
as an argument.
In the next post, I will answer the last question about iterrows()
in pandas, and in a future post I will discuss the difference between functions, methods, and classes.
Leave a Reply