This lesson is from the Real Python video course by Christopher Bailey.
Debugging Code With Decorators
00:00
Now that you’ve timed some functions, how about debugging them? This decorator’s going to name all the arguments that are passed in and out of your function. So, this is our last decorator, @timer
.
00:14
Just above it, if you have the boilerplate there, go ahead and copy it. Then below timer()
, paste that decorator. I’ll leave some extra space. Okay.
00:31
The decorator is going to be named debug()
. It’ll take a function, create a docstring that says
00:43
"""Print the function signature and return value"""
. You’re still decorating your wrapper function with the @functools.wraps()
.
00:52
The wrapper function for the decorator is going to be renamed wrapper_debug()
.
01:04
You’ll assign args_repr
. So, this returns the canonical string representation of the object for a in args
, so this comprehension is going to return all the arguments out of args
, and this is going to create strings for each one.
01:22 You’ll do something very similar with the keyword arguments. Using an f-string.
01:39
So here—don’t forget the s
in .items()
—it’s going to create an iterable out of kwargs
—that dictionary—and return each key in value. Over here, the f-string is going to format them, saying each key is equal to the string representation of the value.
02:01 Next, you create a combined signature.
02:11
It’ll use this string method named .join()
that will concatenate all of these strings together. It will put this in between all of them. So, what are you putting together?
02:20
args_repr
and kwargs_repr
.
02:35
Next, right before calling the function print()
, using an f-string, calling the function’s name using the .__name__
method, and then this is a parentheses in between these two f-string expressions that the signature
then is going to be embedded in. So calling this function using all of these arguments. Great.
02:57
Then, value
will be set to the function with any *args
and **kwargs
coming in, and what happens after? Print another f-string,
03:11
using .__name__
and the repr()
version of it, saying how it returned the values and the .__repr__
of all those—those repr()
versions.
03:22
Next, you’re going return value
, and last, you need to return the wrapper_debug
. Looks good! Now save. Okay. Next up, open your examples
module.
03:39
You are already importing from the decorators
module everything, by using this asterisk (*
). Now, create a new function, decorate it with @debug
.
03:54
This function will be named make_greeting()
. It will take one positional argument, name
, and one keyword argument, age
, which is set to None
by default. Set up a conditional: if age is None:
return this f-string with the expression name
. else:
return with an f-string, using both of these arguments, both age
and name
.
04:23
Okay. So, make_greeting()
requires a name
as an argument, and then also can take an age
. Then, it will print either of these two strings based upon the conditional if
statement there. Go ahead and save.
04:40
Now to use make_greeting()
, you need to start your REPL, and from your examples
module, import make_greeting
. Great. It worked perfect.
04:54
Does it exist? Yep! There it is. Let’s try it out and see how debugging works. First time, say hello to "Benjamin"
. So here, the @debug
decorator said that it’s calling the function make_greeting()
with this argument, 'Benjamin'
.
05:15
'make_greeting'
—the function name—returned 'Howdy Benjamin!'
And at the end here, there’s the string that was returned. It looks like the @debug
decorator’s working well.
05:29
How about if you said hello to "Richard"
? Set an age
of 112
.
05:39
So this time, it shows calling the function with 'Richard'
as our positional argument and the age
key-value argument. 'make_greeting' returned 'Whoa Richard! 112 already, you are growing up!'
And here, you see the f-string below.
05:52
This example might not seem immediately useful, since @debug
the decorator is just repeating what you wrote, but I suggest you use it on a few more complex functions. And there’s another advanced way to use it.
06:05 It’s actually more powerful when you apply it to a small convenience function that you didn’t directly call yourself. Let me show you that as an example.
06:14
It’s going to calculate an approximation of the mathematical constant e. So, you need to go back into your examples.py
module, and I need to import something else to do it. Not only import the decorators, but import also—from the standard library—import math
.
06:46
we’re going to reassign math.factorial
to be a decorated version of it. Notice that @debug
is available, and debug()
is going to call math.factorial
.
06:57
This is kind of going back a little bit in time when we weren’t using the pretty syntactic sugar of the @
symbol. This is how we were using decorators before. You’d take a function and pass it as an argument to your decorator.
07:14
Now, you’re going to approximate e. By default, that’ll have a number of terms of 18
.
07:27
So this is going to return the sum()
of 1
divided by the factorial of n
, for n
in the range()
of the terms
that you’ve entered as an argument. That factorial()
function will be called multiple times, depending on the range()
.
07:43 If you’re interested in learning a little more about the math that’s going on behind here, there’s a link to the Wikipedia article in the written version of the tutorial.
07:53 Okay. Let me save, go ahead and exit the REPL, and restart it.
08:02
And then import approximate_e
. Great. That worked good. Let’s make sure it’s there. Looks good! What happens when you run it? You can set a different number of terms
. Cool!
08:19
So each time that factorial()
runs, you can see it calling it. It’s calling factorial()
of 0
, factorial()
of 1
, 2
, 3
, 4
.
08:30 And you can see the values being returned each time. With the example you just ran, you get a decent approximate value for the true value of e. Another useful tool for debugging functions—besides showing arguments coming in and out—could be slowing down code, especially if you’re using web services. Let me show you that next.
You must own this product to join the conversation.