[Tutor] basic decorator question

Steven D'Aprano steve at pearwood.info
Mon Jul 24 12:01:30 EDT 2017

Hi Bruce,

On Mon, Jul 24, 2017 at 10:33:25AM -0400, bruce wrote:
> Hi.
> I've seen sites discuss decorators, as functions that "wrap" and
> return functions.
> But, I'm sooo confuzed! My real question though, can a decorator have
> multiple internal functions? All the examples I've seen so far have a
> single internal function.

Yes, a decorator can have multiple internal functions. A decorator is 
just a function, and it can contain anything a function contains. What 
makes it specifically a decorator is what you use it for.

Let's step back and cover a basic: nested functions.

def function(x):
    def add_one():
        return x + 1
    def times_two():
        return x*2
    return (add_one(), times_two())


Can you predict what the result of that will be? I hope you can predict 
that it will return (11, 20). Can you see why?

That sort of nested function isn't very interesting, and you won't see 
much code doing that. But it demonstrates that a function can contain 
multiple inner functions. Now let's look at something that is often 
called a *factory function* -- a function which creates and returns a 
new function.

def factory(a):
    def inner(x):
        return a + x
    return inner  # No parentheses!

add_one = factory(1)  # create a new function & assign it to add_one
add_two = factory(2)

add_one(100)  # returns 101
add_two(100)  # returns 102

How this works isn't important (the technical term is "a closure") but 
the important factor is this: factory() creates a new function, and 
returns it. That function can then be used like any other function 
created with "def".

Functions can not only *return* functions as their return result, but 
they can take functions as arguments. We say that "functions are first 
class values" -- in Python, functions are just another kind of data, 
like ints, floats, strings, lists, dicts and more. The most common 
examples of passing a function as input to another function include:

- builtin functions map() and reduce();

- the key argument to sorted() and list.sort();

- GUI libraries that take callback functions;

- and of course, decorators.

So let's start to create our first decorator. A decorator takes a 
function as argument, "decorates" it in some way (usually by adding some 
sort of extra functionality), and then returns the decorated function. 
Here's a simple example: a decorator that makes the other function print 
a message.

def decorate(func):
    def inner(arg):
        print('received argument %r' % arg)
        return func(arg)
    return inner

Now let's set up a couple of functions:

def add_five(x):
    return x+5

def add_ten(x):
    return x+10

And decorate them:

add_five = decorate(add_five)
add_ten = decorate(add_ten)

Try predicting what 

result = add_five(100)

will print, and what the result will be. Likewise for:

result = add_ten(50)

Try running the code and see if you are correct.

The way I decorated the functions above is a little bit clumsy, so 
Python has a special syntax to make it easier: the "@decorate" syntax.

def times_three(x):
    return x*3

result = times_three(5)

Can you predict what that will do?

There's more to decorators than that, but hopefully that will 
demonstrate some of the basic concepts. Feel free to ask any more 
questions on the mailing list, and we will answer if we can.


More information about the Tutor mailing list