[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())
function(10)
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.
@decorate
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.
--
Steve
More information about the Tutor
mailing list