# [Tutor] Function decorators

Cameron Simpson cs at cskk.id.au
Tue Dec 1 23:35:59 EST 2020

```On 02Dec2020 08:03, Manprit Singh <manpritsinghece at gmail.com> wrote:
>For example can you, without considering decorators,
>define a function that returns a new function that
>will calculate a given power?
>
>>>> def exponent_builder2(x):
>           def ypowx(y):
>               return y**x
>           return ypowx
>
>>>> sqr = exponent_builder2(2)
>>>> sqr(7)  # will give 47
>49
>
>cube = exponent_builder2(3)
>>>> cube(3)  #will give 27
>27
>
>This definition can be written in more easy way with lambda expression  as
>below:
>
>>>> def exponent_builder(x):
>           return lambda y: y**x
>
>So,what  is the next exercise for me to understand the function
>decorators?  the code written above by me is correct ? kindly let me
>know

The code's correct. A lambda is just a terse way to write a single
expression function. You need the 'def" form when you have internal
logic which can't be expressed in a single expression, or not expressed
well.

So, to decorators: a decorator is like your functions above, which
return a function. However, they _accept_ a function as their argument,
and return a function to use in its place. Often that returned function
calls the original function in some way.

So consider this:

def trace(func):
def tracer(*a, **kw):
print("calling", func)
value = func(*a, **kw)
print("called", func, "result", value)
return value
return tracer

This defines a function "tracer" which prints a message, calls the
_original_ function with tha same arguments handed to "tracer", then
prints the result and returns the result.

Then we return that "tracer" function, just as you returned "ypowx"
above.

So if you defined some basic function like this:

def square(x):
return x*x

You could rebind the name "square" like this:

square = trace(square)

Now when you call "square" you call the function returned by trace(),
which does some print() calls around the inner call to the original
square() function. In effect, the "square" function is now traced, so
that you can see uses of it.

The reason these are called decorators is that Python has a special
syntax like this:

@trace
def square(x):
return x*x

which is equivalent to going:

def square(x):
return x*x
square = trace(square)

so that the original function is 'decorated' by "@trace".

The returned function doesn't need to call the original function exactly
as it itself was called, not return its value unchanged. You might
adjust any of these depending on what you need.

Cheers,
Cameron Simpson <cs at cskk.id.au>
```