Decorators - CameronAuler/python-devops GitHub Wiki
Table of Contents
Function Decorators
A function decorator is a function that wraps another function to modify its behavior. It is applied using the @decorator_name
syntax before a function definition and it is used for logging, enforcing access control, performance measurement, and more.
Basic Function Decorator
my_decorator
wrapsmy_function
insidewrapper()
.- When
my_function()
is called, it actually callswrapper()
instead. - This allows adding pre-processing and post-processing logic.
def my_decorator(func):
def wrapper():
print("Before function execution")
func()
print("After function execution")
return wrapper
@my_decorator # Equivalent to my_function = my_decorator(my_function)
def my_function():
print("Hello, World!")
my_function()
# Output:
Before function execution
Hello, World!
After function execution
Function Decorator with Arguments
If the decorated function takes arguments, the wrapper
function must accept *args
and **kwargs
.
- The decorator
repeat_decorator
calls the function twice. *args, **kwargs
allow it to handle any function arguments.
def repeat_decorator(func):
def wrapper(*args, **kwargs):
print("Function is being executed twice:")
func(*args, **kwargs)
func(*args, **kwargs)
return wrapper
@repeat_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
# Output:
Function is being executed twice:
Hello, Alice!
Hello, Alice!
Function Decorator with Parameters
To pass arguments to the decorator itself, wrap it inside another function.
- The
repeat(n)
function returns a decorator that callsfunc()
n
times.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3) # Repeat function 3 times
def say_hello():
print("Hello!")
say_hello()
# Output:
Hello!
Hello!
Hello!
Class Decorators
A class decorator is a class that wraps a function or another class.
Class as a Decorator
The __call__
method allows instances of the class to be used as decorators.
Logger
class stores the function (self.func
).__call__
executesself.func
and logs its call.
class Logger:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f"Calling function: {self.func.__name__}")
return self.func(*args, **kwargs)
@Logger
def add(a, b):
return a + b
print(add(2, 3))
# Output:
Calling function: add
5
Using Class Decorators to Track Calls
- The
CallCounter
decorator counts how many times a function is called.
class CallCounter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Function {self.func.__name__} has been called {self.count} times.")
return self.func(*args, **kwargs)
@CallCounter
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
greet("Bob")
# Output:
Function greet has been called 1 times.
Hello, Alice!
Function greet has been called 2 times.
Hello, Bob!
functools.wraps
When a function is decorated, the original function’s metadata (name, docstring, etc.) is overwritten by the wrapper. functools.wraps
can be used to fix the metadata.
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before function execution")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""This function says hello."""
print("Hello!")
print(say_hello.__name__) # Outputs "wrapper" instead of "say_hello"
print(say_hello.__doc__) # None
# Output:
wrapper
None
functools.wraps
Fixing the Metadata with functools.wraps
preserves the original function’s metadata.
@wraps(func)
tells Python to copy metadata fromfunc
towrapper
.
from functools import wraps
def my_decorator(func):
@wraps(func) # Preserves metadata
def wrapper(*args, **kwargs):
print("Before function execution")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""This function says hello."""
print("Hello!")
print(say_hello.__name__) # Outputs "say_hello"
print(say_hello.__doc__) # Outputs correct docstring
# Output:
say_hello
This function says hello.