DEV Community

Michael Tanaka
Michael Tanaka

Posted on

Understanding Python Decorators: A Practical Introduction

What Are Decorators?

In Python, functions are first-class citizens. This means they can be passed around as arguments to other functions, returned as values from other functions, and even assigned to variables. Decorators leverage this feature to extend or modify the behavior of target functions.

Basic Syntax

The basic syntax for decorators involves the "@" symbol, followed by the name of the decorator function. This is placed just above the function intended to be decorated. Think of decorator syntax as syntactic sugar.

@decorator_function
def target_function():
    print("Target function executed.")
Enter fullscreen mode Exit fullscreen mode

How Does It Work?

When you use a decorator, you are essentially doing three things:

Passing the target function as an argument to the decorator function.
Modifying or extending the behavior of the target function within the decorator function.
Returning the modified function or even a completely new function from the decorator function.

def decorator_function(target_function):
    def wrapper_function():
        print("Before the target function.")
        target_function()
        print("After the target function.")
    return wrapper_function

@decorator_function
def say_hello():
    print("Hello!")

say_hello()
Enter fullscreen mode Exit fullscreen mode

When you call say_hello(), it actually executes wrapper_function(). This, in turn, calls the original say_hello() function between two print statements. The output would be:

Before the target function.
Hello!
After the target function.
Enter fullscreen mode Exit fullscreen mode

Chaining Decorators

Decorators can be chained, meaning you can apply multiple decorators to a single function:

def decorator_one(func):
    def wrapper_one():
        print("Decorator one: Before calling the function.")
        func()
        print("Decorator one: After calling the function.")
    return wrapper_one

def decorator_two(func):
    def wrapper_two():
        print("Decorator two: Before calling the function.")
        func()
        print("Decorator two: After calling the function.")
    return wrapper_two

@decorator_one
@decorator_two
def my_function():
    print("The original function.")

my_function()
Enter fullscreen mode Exit fullscreen mode

Here, my_function is first passed to decorator_two, and then the result is passed on to decorator_one. The output for this code would be:

Decorator one: Before calling the function.
Decorator two: Before calling the function.
The original function.
Decorator two: After calling the function.
Decorator one: After calling the function.
Enter fullscreen mode Exit fullscreen mode

Class Decorators

During my time at Flatiron School, I came across a few useful class decorators:

@staticmethod

  • A static method is bound to the class itself and not to an instance of the class.
  • These methods serve as utility functions that operate on their parameters.

Example:

class MyClass:
    def __init__(self,name):
        self.name = self.capitalize_name(name)

    @staticmethod
    def capitalize_name(name):
        return name.capitalize()

instance1 = MyClass('john')
print(instance1.name)
Enter fullscreen mode Exit fullscreen mode

The output would be:

John
Enter fullscreen mode Exit fullscreen mode

@classmethod

  • Similar to a static method, a class method is bound to the class and not an instance of the class.
  • The first parameter for a class method is cls, which stands for 'class'.
  • Class methods can access or modify the class state.

Example:

class MyClass:
    def __init__(self,name):
        self.name = name

    @classmethod 
    def create_with_capitalized_name(cls,name):
        name = name.capitalize()
        return cls(name)

instance1 = MyClass.create_with_capitalized_name('john')
print(instance1.name)
Enter fullscreen mode Exit fullscreen mode

The output would be:

'John'
Enter fullscreen mode Exit fullscreen mode

There's a whole world of decorators out there, but these are the key ones that I've encountered on my Python learning journey at Flatiron School. I genuinely hope you've found this article to be a helpful guide. I'm always open to feedback, so feel free to share your thoughts. Thanks so much for taking the time to read!

Top comments (0)