Files
python-doc/Docs/Basic/08-decorators.md
2026-02-04 21:23:37 +03:30

3.3 KiB
Raw Blame History

08 Decorators in Python

This document explains decorators, how they work, and how they are used to extend function behavior without modifying the original function code.


1. What Is a Decorator?

A decorator is a function that:

  • Takes another function as input
  • Adds extra behavior
  • Returns a new function

Decorators are commonly used for:

  • Input validation
  • Logging
  • Authentication
  • Performance measurement
  • Access control

2. Basic Decorator Structure

A decorator has three layers:

  1. The decorator function
  2. The wrapper function
  3. The original function

General Pattern

def decorator(func):
    def wrapper(*args, **kwargs):
        # extra behavior
        return func(*args, **kwargs)
    return wrapper

3. Example: Input Validation Decorator

Code

def check_number(func):
    def wrapper(a, b):
        if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
            print("Input must be numbers")
            return
        return func(a, b)
    return wrapper

Explanation

  • check_number is the decorator.
  • func is the function being decorated.
  • wrapper replaces the original function.
  • a and b are the arguments passed to the original function.
  • isinstance(a, (int, float)) ensures inputs are numeric.
  • If validation fails, execution stops.
  • If validation passes, the original function is called.

4. Using the Decorator with @ Syntax

Code

@check_number
def bemola(a, b):
    try:
        res = a / b
        print(res)
    except ZeroDivisionError:
        print("Zero Number Detected")
    except Exception as e:
        print(f"Error Detected {e}")

What Happens Internally

This line:

@check_number

Is equivalent to:

bemola = check_number(bemola)

The function bemola is replaced by wrapper.


5. Execution Flow

When calling:

bemola(10, 2)

The flow is:

  1. wrapper(10, 2) is called
  2. Inputs are validated
  3. func(10, 2) is executed
  4. Result is printed

If calling:

bemola(10, "a")

The output will be:

Input must be numbers

6. Why Use Decorators?

Without decorators, input validation would need to be repeated in every function.

Decorators allow:

  • Reusable logic
  • Cleaner code
  • Separation of concerns

7. Limitations in This Example

  • The decorator only works with exactly two arguments.
  • It does not preserve the original functions metadata (__name__, __doc__).

8. Improved Version (Best Practice)

from functools import wraps

def check_number(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if not all(isinstance(x, (int, float)) for x in args):
            print("Input must be numbers")
            return
        return func(*args, **kwargs)
    return wrapper

Improvements

  • Supports any number of arguments
  • Preserves function name and documentation
  • More reusable and professional

Summary

  • Decorators modify function behavior without changing its code
  • They wrap functions inside another function
  • @decorator is syntactic sugar
  • Commonly used for validation, logging, and access control
  • Best practice is to use *args, **kwargs, and functools.wraps