Built with Tutify.io

    AIStudyLabs

    Decorators: Functions that Modify Functions

    Decorators: Functions that Modify Functions

    Introduction

    In this lesson, we will delve into the advanced concept of decorators in Python. Decorators are design patterns that allow the modification of functions or methods using other functions. This lesson builds on your understanding of higher-order functions by showcasing how decorators can alter the functionality of existing code without modifying the original function's structure. We will explore fundamental decorator syntax, using the @decorator notation, learn how to write decorators that accept arguments, and ensure we preserve a decorated function's metadata with functools.wraps. Additionally, we will examine the use of multiple decorators and their order of execution.

    How Decorators Work

    Decorators are essentially higher-order functions that take another function as an argument and return a new function with enhanced features. Conceptually, a decorator wraps a function and adds new behavior to it without changing its source code.

    Basic Decorator Implementation

    To implement a basic decorator, we define a function that returns a wrapper function. The @decorator syntax is a Pythonic shorthand for applying decorators.

    def my_decorator(func):
        def wrapper():
            print("Something happens before the function.")
            func()
            print("Something happens after the function.")
        return wrapper
    
    @my_decorator
    def say_hello():
        print("Hello!")
    
    say_hello()
    
    // Output:
    Something happens before the function.
    Hello!
    Something happens after the function.
    

    Decorators with Arguments

    Sometimes, decorators need to accept arguments. This is achieved by nesting an additional function level.

    def repeat(num_times):
        def decorator_repeat(func):
            def wrapper(*args, **kwargs):
                for _ in range(num_times):
                    func(*args, **kwargs)
            return wrapper
        return decorator_repeat
    
    @repeat(num_times=3)
    def greet(name):
        print(f"Hello, {name}!")
    
    greet("Alice")
    
    // Output:
    Hello, Alice!
    Hello, Alice!
    Hello, Alice!
    

    Preserving Function Metadata

    When decorators modify functions, the original function's metadata (name, docstring, etc.) may be lost. To avoid this, use the functools.wraps decorator from the functools module.

    import functools
    
    def my_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print("Decorator is called.")
            return func(*args, **kwargs)
        return wrapper
    
    @my_decorator
    def example_func():
        """This is an example function."""
        print("Example function is running.")
    
    print(example_func.__name__)
    print(example_func.__doc__)
    
    // Output:
    example_func
    This is an example function.
    

    Applying Multiple Decorators

    When using multiple decorators, they are applied from the innermost to the outermost. This order of application can affect the result.

    def bold(func):
        def wrapper():
            return f"<b>{func()}</b>"
        return wrapper
    
    def italic(func):
        def wrapper():
            return f"<i>{func()}</i>"
        return wrapper
    
    @bold
    @italic
    def text():
        return "Decorated Text"
    
    print(text())
    
    // Output:
    <b><i>Decorated Text</i></b>
    

    Summary / Key Takeaways

    By mastering decorators, you can build flexible, clean, and maintainable Python applications, leveraging the power of function modification with minimal code redundancy.

    Python Programming – Complete Course with Practical Examples — Advanced Python: Iterators, Generators, and Decorators

    Getting Started with Python and the Course

    Python Syntax, Indentation, and Core Concepts

    Variables, Data Types, and Type Conversion

    Operators, Expressions, and Control Flow

    Loops and Iteration Patterns

    Working with Strings in Depth

    Collections: Lists, Tuples, Sets, and Dictionaries

    Functions and Program Structuring

    Functional Programming Concepts in Python

    Modules, Packages, and Code Organization

    Error Handling and Debugging Fundamentals

    File Handling and Data Formats

    Object-Oriented Programming in Python

    Advanced Python: Iterators, Generators, and Decorators

    Python Standard Library for Real-World Tasks

    Python for Automation and Scripting

    Testing and Quality Assurance in Python

    Tutorial Not Found