The decorator system in Python is built to pass the function being decorated (unstable_function) automatically to the decorator. This is part of the Python syntax for decorators. Here’s how it works:
def my_decorator(func):
...
And use it as @my_decorator, Python internally rewrites it to:
func = my_decorator(func)
@retry(max_retries=3, delay=1)
def unstable_function():
...
unstable_function = retry(max_retries=3, delay=1)(unstable_function)
def logging_decorator(func):
# The decorator receives the original function as an argument
def wrapper(*args, **kwargs):
# This is the replacement function (the wrapper)
print(f"Function {func.__name__} was called with arguments {args} and {kwargs}")
result = func(*args, **kwargs) # Call the original function
return result # Return its result
return wrapper # Return the wrapper function itself
@logging_decorator
def multiply(x, y):
return x * y
# Step-by-step:
# multiply = logging_decorator(multiply)
# Now `multiply` is actually `wrapper`.
result = multiply(4, 5)
# This calls `wrapper(4, 5)`.
# Inside `wrapper`:
# - It logs: "Function multiply was called with arguments (4, 5) and {}"
# - Calls the original `multiply(4, 5)`
# - Returns the result (20)
print(result) # Output: 20
How It Works
Outer Function (retry):
Accepts max_retries and delay as arguments.
Returns the actual decorator (decorator).
Decorator Function (decorator):
Accepts the target function (func) as an argument.
Returns the wrapper.
Wrapper Function (wrapper):
Contains the retry logic.
Tries to execute the target function up to max_retries times.
Prints messages for failed attempts and retries.
Raises an exception if all attempts fail.
import random
import time
def retry(max_retries=3, delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_retries:
try:
return func(*args, **kwargs) # Try to execute the function
except Exception as e:
attempts += 1
print(f"Attempt {attempts} failed with error: {e}. Retrying...")
time.sleep(delay) # Optional delay between retries
raise RuntimeError(f"Function failed after {max_retries} retries.")
return wrapper
return decorator
@retry(max_retries=3, delay=2)
def unstable_function():
if random.random() < 0.7: # 70% chance of failing
raise ValueError("Something went wrong!")
print("Success!")
# Call the function
unstable_function()