Yandex

Python Decorators



Introduction to Python Decorators

Decorators in Python are a powerful tool that allows you to modify the behavior of functions or classes. If you're coming from a beginner’s perspective, think of a decorator as a "wrapper" that adds extra features to an existing function without changing its core code.

Why Use Decorators?

Imagine you want to log the execution time of various functions or check user authentication before executing a task. You could repeat that logic inside every function... or, you could just use a decorator. It keeps your code clean, modular, and reusable.

Functions are First-Class Citizens

Before diving into decorators, it’s important to know that in Python, functions are first-class objects. This means:

  • You can pass a function as an argument.
  • You can return a function from another function.
  • You can assign a function to a variable.
def greet():
    return "Hello!"

say_hello = greet
print(say_hello())
Hello!

Basic Decorator Structure

A decorator is simply a function that takes another function as input and returns a new function with added behavior.

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

def say_hi():
    print("Hi!")

decorated = my_decorator(say_hi)
decorated()
Something is happening before the function is called.
Hi!
Something is happening after the function is called.

Using the @ Syntax (Syntactic Sugar)

Python provides a cleaner way to apply decorators using the @ symbol.

@my_decorator
def say_hi():
    print("Hi!")

say_hi()
Something is happening before the function is called.
Hi!
Something is happening after the function is called.

Decorators with Arguments

If your target function has parameters, your wrapper needs to accept them too.

def log_args(func):
    def wrapper(*args, **kwargs):
        print(f"Arguments passed: {args}, {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_args
def add(x, y):
    return x + y

print(add(5, 7))
Arguments passed: (5, 7), {}
12

Returning Values from Decorated Functions

Always remember to return the result of the original function if it produces one.

Using functools.wraps

When you use decorators, the metadata of the original function (like its name or docstring) can get lost. To preserve it, use functools.wraps.

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@my_decorator
def greet(name):
    "Greets a user by name"
    print(f"Hello, {name}")

print(greet.__name__)
print(greet.__doc__)
Before function call
Hello, Alice
After function call
greet
Greets a user by name

Common Use Cases of Decorators

  • Logging
  • Access control (authentication/authorization)
  • Timing functions
  • Memoization (caching)
  • Validation

Built-in Decorators in Python

Python includes several built-in decorators:

  • @staticmethod
  • @classmethod
  • @property
class MyClass:
    @staticmethod
    def static_method():
        print("This is a static method.")

    @classmethod
    def class_method(cls):
        print(f"This is a class method from {cls.__name__}")

    @property
    def name(self):
        return "TutorialKart"

obj = MyClass()
obj.static_method()
obj.class_method()
print(obj.name)
This is a static method.
This is a class method from MyClass
TutorialKart

Best Practices & Things to Watch For

  • Always use functools.wraps when writing decorators.
  • Make sure your decorator does not block or break return values.
  • Understand that decorators are executed at the time of function definition, not at runtime.
  • Use decorators to separate logic concerns (e.g., don’t mix validation and logging inside the core function).

Verification Steps

To ensure your decorator works as expected:

  1. Call the decorated function and check if the new behavior is added.
  2. Check that arguments and return values are intact.
  3. Verify metadata using __name__ and __doc__.
  4. Use logging statements in the decorator to debug step-by-step.

Conclusion

Decorators are one of Python’s elegant ways to inject functionality without altering existing code. Whether you’re logging, validating, or checking permissions — they let you write cleaner, modular code that feels both powerful and Pythonic. Now that you've mastered the basics, go ahead and create your own decorators to supercharge your functions!



Welcome to ProgramGuru

Sign up to start your journey with us

Support ProgramGuru.org

You can support this website with a contribution of your choice.

When making a contribution, mention your name, and programguru.org in the message. Your name shall be displayed in the sponsors list.

PayPal

UPI

PhonePe QR

MALLIKARJUNA M