Decorators in Python (original) (raw)

Decorators are flexible way to modify or extend behavior of functions or methods, without changing their actual code.

python_decorator

Python Decorators

**Example: Below is an example to demonstrate how decorator functions:

Python `

def decorator(func): def wrapper(): print("Before calling the function.") func() print("After calling the function.") return wrapper

@decorator def greet(): print("Hello, World!") greet()

`

Output

Before calling the function. Hello, World! After calling the function.

**Explanation:

Decorator with Parameters

Decorators often need to work with functions that have arguments. We use *args and **kwargs so our wrapper can accept any number of arguments.

**Example: Let's see an example of a decorator that adds functionality before and after a function call.

Python `

def decorator_name(func): def wrapper(*args, **kwargs): print("Before execution") result = func(*args, **kwargs) print("After execution") return result return wrapper

@decorator_name def add(a, b): return a + b

print(add(5, 3))

`

Output

Before execution After execution 8

**Explanation:

Functions as First-Class Objects

Functions are first-class objects, meaning they can be treated like any other object (such as integers, strings or lists). This allows functions to be assigned to variables, passed as arguments, returned from other functions and stored in data structures, including decorators.

**Example: This code demonstrates all four properties of functions as first-class objects.

Python `

Assigning a function to a variable

def greet(n): return f"Hello, {n}!" say_hi = greet print(say_hi("Alex"))

Passing a function as an argument

def apply(f, v): return f(v) res = apply(say_hi, "Elon") print(res)

Returning a function from another function

def make_mult(f): def mult(x): return x * f return mult dbl = make_mult(2) print(dbl(5))

`

Output

Hello, Alex! Hello, Elon! 10

**Explanation:

**Role of First-Class Functions in Decorators:

Higher-Order Functions

Higher-order functions are functions that either take other functions as input, return a function as output, or both. They allow functions to be treated like data, making code more flexible and reusable.

**Example: This code shows a higher-order function that takes another function as an argument and applies it to a given value.

Python `

def fun(f, x): return f(x)

def square(x): return x * x res = fun(square, 5) print(res)

`

**Explanation: fun is a higher-order function because it takes another function f as an argument and applies it to the value x.

Role of Higher-Order Functions in Decorators: Decorators are higher-order functions that take a function, modify it and return a new one with extended or altered behavior.

Types of Decorators

1. Function Decorators: used to wrap and enhance functions by adding extra behavior before or after the original function runs.

**Example: In this Example, a decorator prints a message before and after executing wrapped function.

Python `

def simple_decorator(func): def wrapper(): print(">>> Starting function") func() print(">>> Function finished") return wrapper

@simple_decorator def greet(): print("Hello, World!") greet()

`

Output

Starting function Hello, World! Function finished

**Explanation:

2. Method Decorators: Special decorators used for methods inside a class. They work like function decorators but handle the self parameter for instance methods.

**Example: Here, a decorator prints a message before and after a method is executed, while correctly handling self argument.

Python `

def method_decorator(func): def wrapper(self, *args, **kwargs): print("Before method execution") res = func(self, *args, **kwargs) print("After method execution") return res return wrapper

class MyClass: @method_decorator def say_hello(self): print("Hello!") obj = MyClass() obj.say_hello()

`

Output

Before method execution Hello! After method execution

**Explanation:

3. Class Decorators: used to modify or enhance behavior of a class. Like function decorators, class decorators are applied to class definition. They work by taking class as an argument and returning a modified version of class.

**Example: This code demonstrates a class decorator that adds a class_name attribute to a class, storing class’s name.

Python `

def fun(cls): cls.class_name = cls.name return cls

@fun class Person: pass print(Person.class_name)

`

**Explanation:

Built-in Decorators

1. @staticmethod: used to define a method that doesn't operate on an instance of class (i.e., it doesn't use self). Static methods are called on class itself, not on an instance of class.

**Example: This example shows how to define and use a @staticmethod inside a class.

Python `

class MathOperations: @staticmethod def add(x, y): return x + y

res = MathOperations.add(5, 3) print(res)

`

**Explanation:

2. @classmethod: used to define a method that operates on class itself (i.e., it uses cls). Class methods can access and modify class state that applies across all instances of class.

**Example: This code defines a class Employee with a class variable raise_amount and a class method set_raise_amount that updates this variable for entire class.

Python `

class Employee: raise_amount = 1.05 def init(self, name, salary): self.name = name self.salary = salary

@classmethod
def set_raise_amount(cls, amount):
    cls.raise_amount = amount

Employee.set_raise_amount(1.10) print(Employee.raise_amount)

`

**Explanation:

3. @property: used to define a method as a property, which allows to access it like an attribute. This is useful for encapsulating implementation of a method while still providing a simple interface.

**Example: This code defines a circle class demonstrating @property for controlled attribute access, allowing safe updates to radius.

Python `

class Circle: def init(self, radius): self._radius = radius

@property
def radius(self):
    return self._radius

@radius.setter
def radius(self, value):
    if value >= 0:
        self._radius = value
    else:
        raise ValueError("Radius cannot be negative")

@property
def area(self):
    return 3.14159 * (self._radius ** 2)

c = Circle(5) print(c.radius) print(c.area)
c.radius = 10 print(c.area)

`

**Explanation:

Chaining Multiple Decorators

Chaining decorators means applying multiple decorators to same function. Each decorator wraps function in sequence, adding layered behavior.

**Example: This example shows how chaining decorators works by applying two decorators in different orders to see how output changes.

Python `

def decor1(func): def inner(): x = func() return x * x return inner

def decor(func): def inner(): x = func() return 2 * x return inner

@decor1 @decor def num(): return 10

@decor @decor1 def num2(): return 10

print(num()) print(num2())

`

**Explanation:

Application of Decorators

  1. Logging: Track function calls (e.g., @logger).
  2. Authentication: Restrict access in web apps (e.g., Flask/Django).
  3. Rate Limiting: Control API usage per user.
  4. Caching: Store results using functools.lru_cache.
  5. Retry Logic: Automatically retry failed network calls.