1

I wrote a small piece of code to run it myself and understand the flow:

def perform_arithmetic_ops(func):
    print("INSIDE PERFORM ARTITHMETIC")
    def decorate(*args):
        print("I JUST GOT DECORATED")
        print(f"SUM -> {args[0]+args[1]}")
        func(args[0],args[1])
    return decorate


@perform_arithmetic_ops
def print_me(x,y):
    print(f"X = {x} ; Y = {y}")

print_me(5,4)

Please correct me if my understanding, which I've summarized in the form points below, is incorrect.

  1. def perform_arithmetic_ops is the decorator which only takes a callable object, in this case a function as its argument, correct?

  2. The inner function def decorate() here is what actually performs the desired modification. What I noticed was commenting func(args[0],args[1]) inside def decorate() basically meant that the X={x} ; Y={y} statement wasn't being printed. So does this mean that the the decorator replaces everything the original function does? If yes, how is that a modification? Isn't it more of a replacement for another function?

  3. return decorate() i.e. calling decorate() instead of returning it led to errors for tuple index out of range. Why?

ruohola
  • 21,987
  • 6
  • 62
  • 97
Dhiwakar Ravikumar
  • 1,983
  • 2
  • 21
  • 36
  • 1. Yes. 2. You're wrapping the function, not modifying it, so if you don't call it you won't see the expected results. 3. Because you're calling it with empty `args`, so it doesn't have what it needs to pass to the wrapped function. – jonrsharpe Apr 28 '19 at 10:06
  • @jonrsharpe, thank you. – Dhiwakar Ravikumar Apr 28 '19 at 10:10

1 Answers1

2

Take a good look at this enormous answer/novel. It's one of the best explanations I've come across.

def perform_arithmetic_ops is the decorator which only takes a callable object, in this case a function as its argument, correct?

Yes. You are right about it.

The inner function def decorate() here is what actually performs the desired modification. What I noticed was commenting func(args[0],args[1]) inside def decorate() basically meant that the X={x} ; Y={y} statement wasn't being printed. So does this mean that the the decorator replaces everything the original function does? If yes, how is that a modification? Isn't it more of a replacement for another function?

A decorator is a function that takes a function as its only parameter and returns a function. This is helpful to “wrap” functionality with the same code over and over again. It is wrapping but not modification.

return decorate() i.e. calling decorate() instead of returning it led to errors for tuple index out of range. Why?

To understand decorators, you must first understand that functions are objects in Python.

Because you might be calling it without any parameters. Try this return decorate(5,1). But after this statement and if you try to decorate function like print_me, then print_me might be holding non-callable object. See this:

def perform_arithmetic_ops(func):
    print("INSIDE PERFORM ARTITHMETIC")
    def decorate(*args):
        print("I JUST GOT DECORATED")
        print(f"SUM -> {args[0]+args[1]}")
        return func(args[0], args[1])
    return decorate(5, 1)


@perform_arithmetic_ops
def print_me(x,y):
    print(f"X = {x} ; Y = {y}")
    return 5


# print_me(5,2) # Will throw: TypeError: 'int' object is not callable
print(print_me)
ruohola
  • 21,987
  • 6
  • 62
  • 97
abhiarora
  • 9,743
  • 5
  • 32
  • 57