Python Decorators: Understanding Function and Class Decorators
Introduction to Python Decorators
Decorators are a powerful feature in Python that allows you to modify or extend the behavior of functions or classes without changing their actual code. Decorators are widely used for logging, access control, instrumentation, and more.
This tutorial will focus on understanding function decorators and class decorators through examples, with minimal theory.
Function Decorators
Function decorators are functions that modify the behavior of other functions. They are defined using the @decorator_name syntax.
Example 1: Basic Function Decorator
Code:
# A simple decorator function
def simple_decorator(func):
def wrapper():
print("Before the function call") # Code to execute before the function
func() # Call the original function
print("After the function call") # Code to execute after the function
return wrapper # Return the wrapper function
# Applying the decorator to a function
@simple_decorator
def say_hello():
print("Hello!")
# Calling the decorated function
say_hello()
Output:
Before the function call Hello! After the function call
Explanation:
Decorator Function ('simple_decorator'):
- The 'simple_decorator' function takes another function ('func') as its argument.
- Inside it, the wrapper function is defined, which adds behavior before and after calling the original function.
Using the Decorator:
- The '@simple_decorator' syntax is used to apply the decorator to the 'say_hello' function.
- When 'say_hello' is called, it actually executes the wrapper function, printing messages before and after the original ‘say_hello’ function is called.
Example 2: Function Decorator with Arguments
Code:
# Decorator that accepts arguments
def decorator_with_args(func):
def wrapper(*args, **kwargs):
print("Arguments passed to the function:", args, kwargs) # Log arguments
result = func(*args, **kwargs) # Call the original function
print("Function result:", result) # Log the result
return result
return wrapper
# Applying the decorator to a function
@decorator_with_args
def add(x, y):
return x + y
# Calling the decorated function with arguments
add(10, 22)
Output:
Arguments passed to the function: (10, 22) {} Function result: 32
Explanation:
Decorator with Arguments ('decorator_with_args'):
- The wrapper function accepts '*args' and ‘**kwargs' to handle any number of positional and keyword arguments.
- The decorator logs the arguments and the result of the function call.
Using the Decorator:
- The '@decorator_with_args' syntax is used to apply the decorator to the add function.
- When 'add(10,22)' is called, the decorator prints the arguments and the result.
Example 3: Function Decorator with Arguments for the Decorator
Code:
# Decorator factory that takes arguments
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times): # Repeat the function call `times` times
result = func(*args, **kwargs)
return result
return wrapper
return decorator
# Applying the decorator with an argument
@repeat(5)
def greet(name):
print(f"Hello, {name}!")
# Calling the decorated function
greet("Philemon")
Output:
Hello, Philemon! Hello, Philemon! Hello, Philemon! Hello, Philemon! Hello, Philemon!
Explanation:
Decorator Factory ('repeat'):
- The 'repeat' function takes an argument ('times') and returns a decorator ('decorator').
- The 'wrapper' function calls the original function multiple times, depending on the value of times.
Class Decorators:
Class decorators modify or extend the behavior of classes in a similar way to function decorators.
Example 4: Basic Class Decorator
Code:
# A simple class decorator
def class_decorator(cls):
class WrappedClass:
def __init__(self, *args, **kwargs):
self.instance = cls(*args, **kwargs) # Instantiate the original class
def __getattr__(self, name):
return getattr(self.instance, name) # Delegate attribute access to the original instance
def new_method(self):
return "This is a new method added by the decorator!"
return WrappedClass
# Applying the decorator to a class
@class_decorator
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, my name is {self.name} and I'm {self.age} years old."
# Creating an instance of the decorated class
person = Person("Sajra", 30)
# Calling methods
print(person.greet())
print(person.new_method())
Output:
Hello, my name is Sajra and I'm 30 years old. This is a new method added by the decorator!
Explanation:
Class Decorator ('class_decorator'):
- The decorator function wraps the original class in a new class ('WrappedClass').
- '__getattr__' is used to delegate attribute access to the original instance.
- A new method 'new_method' is added to the class.
Using the Decorator:
- The '@class_decorator' syntax is used to apply the decorator to the Person class.
- The greet method behaves as usual, while the 'new_method' demonstrates the added functionality.
Example 5: Class Decorator with State
Code:
# Class decorator that adds state
def add_state(cls):
class WrappedClass:
def __init__(self, *args, **kwargs):
self.instance = cls(*args, **kwargs)
self.decorator_state = 0 # Initial state added by the decorator
def __getattr__(self, name):
return getattr(self.instance, name)
def increment_state(self):
self.decorator_state += 1 # Method to modify the state
return self.decorator_state
return WrappedClass
# Applying the decorator to a class
@add_state
class Calculator:
def add(self, x, y):
return x + y
# Creating an instance of the decorated class
calc = Calculator()
# Calling methods
print(calc.add(10, 25))
print(calc.increment_state())
print(calc.increment_state())
Output:
35 1 2
Explanation:
Class Decorator with State ('add_state'):
- This decorator adds a new state (‘decorator_state’) to the original class.
- It also provides a method (‘increment_state’) to modify and access this state.
Using the Decorator:
- The '@add_state' syntax applies the decorator to the Calculator class.
- The add method functions normally, while 'increment_state' manages the decorator's state.
Summary:
- Function Decorators: Modify or extend functions' behavior. They can be simple or complex, handling arguments or controlling multiple function calls.
- Class Decorators: Modify or extend classes' behavior, adding new methods, managing state, or wrapping the original class.
- Common Patterns:
- Wrapper Functions: Used to add behavior before and after the original function or class method.
- Decorator Factories: Decorators that take arguments to control their behavior.
Decorators are a powerful tool in Python, allowing you to write cleaner, more maintainable, and more flexible code by abstracting repetitive or cross-cutting concerns.
- Weekly Trends and Language Statistics
- Weekly Trends and Language Statistics