@decorator

@decorator

What is a decorator and how it works?

  • Python’s decorators allow you to extend and modify the behavior of a callable (functions, methods, and classes) without permanently modifying the callable itself.
  • Decorators should use the @functools.wraps decorator, which will preserve information about the original function. Preserves original name of the function.
# Functions returning Functions 

def f(x):
		def g(y):
				return y + x + 3
		return g

nf1 = f(1) # func = decorator(func)
nf2 = f(3)

print(nf1(1)) # 5
print(nf2(1)) # 7
Example 2

Decorator examples

Simple examples
Decorating functions that accept arguments
Decorator with argument @repeat(num_times=4)

Advanced decorator examples

Decorator with Class
Decorator as Class

GitHub examples

# EXAMPLE 1: Simple example without argument
import functools

def require_authorization(f):
    @functools.wraps(f)
    def decorated(user, *args, **kwargs):
        if not is_authorized(user):
            raise UserIsNotAuthorized
        return f(user, *args, **kwargs)
    return decorated

@require_authorization
def check_email(user, etc):
    # etc.
# EXAMPLE 2: Decorator factory (passing args to decorators)
def require_authorization(action):
    def decorate(f):
        @functools.wraps(f):
        def decorated(user, *args, **kwargs):
            if not is_allowed_to(user, action):
                raise UserIsNotAuthorized(action, user)
            return f(user, *args, **kwargs)
        return decorated
    return decorate

Use cases

Memorization
Sharing data between decorators using SELF

Ways a Decorator Modifies Behavior

A decorator can change a function in several powerful ways:

  • Pre-processing: Checking if a user is logged in before allowing the function to run (Authentication).
  • Post-processing: Converting a function's return value into a different format (like JSON or HTML).
  • Rate Limiting: Checking how many times a function has been called and stopping it if it’s too frequent.
  • Caching (Memoization): Checking if the function has been called with these specific arguments before. If it has, the decorator returns the saved result instead of running the function again.
  • Error Handling: Wrapping the function in a try...except block to log errors globally.

Why is this better than just editing the function?

  1. Don't Repeat Yourself (DRY): If you have 50 functions that all need logging, you only write the logging logic once in the decorator.
  2. Separation of Concerns: for example the add_numbers function should only care about math. It shouldn't have to care about logging, security, or database connections. The decorator handles the "plumbing," while the function handles the "logic."

References

SuperMade with Super