2

Why does the decorator function need to define its own function instead of just running the code...So instead of this:

def decor(func):
    def wrap():
        print("============")
        func()
        print("============")
    return wrap

def print_text():
    print("Hello world!")

decorated = decor(print_text)
decorated()

Why not this:

def decor(func):
    print("============")
    func()
    print("============")

def print_text():
    print("Hello world!")

decorated = decor(print_text)
decorated
Dima
  • 145
  • 7
  • 1
    Also, think about how you would pass additional arguments to the decorated function... – ephemient Apr 28 '17 at 02:19
  • Could you explain what you mean? – Dima Apr 28 '17 at 02:21
  • `def welcome(arg): print(\`Hello ${arg}!\`)` `decorated = decor(welcome)` You can run `welcome('world')`. How do you run `decorated('world')`? – ephemient Apr 28 '17 at 02:26
  • Are you saying don't use decorators if the main function needs custom arguments? – Dima Apr 28 '17 at 02:32
  • No. I'm saying that the Python way makes it easier to write generic decorators (that can create wrapper functions that handle arbitrary arguments) than your proposal. – ephemient Apr 28 '17 at 02:33

2 Answers2

3

The purpose of the decorator syntax is to apply a function to a callable, and produce another callable. In your second example, you don't really have a decorator; you are simply passing a function reference to another function that calls it.

Put another way, the defining feature of a function intended to be used with decorator syntax is that it returns another function. There is nothing special about the decorator syntax; it is syntactic sugar, and it doesn't do anything that you couldn't accomplish with regular Python syntax. The following

@foo
def bar(...):
    ...

is exactly equivalent to, but more concise than, the more explicit form

def bar(...):
    ...

bar = foo(bar)
chepner
  • 389,128
  • 51
  • 403
  • 529
1

Your 2nd code block can "get the job done" each time it is evaluated. What if you need to do this many times? Each time you must evaluate the expression decor(some_function), even if the decorated function, some_function, happens to be the same thing. What if the context of its evaluation isn't fully in your control?

With the 1st approach, you basically wrap the decorated function in the same thunk that can be evaluated later. You than call the same thunk each time you need it called. You can even delegate the call to someone else.

Your decorator example is trivial. When the code gets more complex, Python's ability to return a function as first-class object is a boon. An example would be that an API expects a callback:

def frob(some, args, callback=your_function):

Now, you've written your callback and you discover that the performance can be boosted by memoization. With the decorator pattern you can do things like

my_function_memoized = memoize(my_function, *even_more_parameters_controlling_the_memoization_behavior)  # returns decorate function
... ...
frob(some, args, callback=my_function_memoized)

That is to say, the decorated thunk can be used just in the place of my_function. This happens a lot in Python, because Python makes it very easy to pass callables around.

Cong Ma
  • 8,782
  • 2
  • 24
  • 46