[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>


More information about the Tutor mailing list