This absolutely excellent talk by James Powell prompted this. It’s entirely worth the watch.
Why Use Decorators
Decorators allow you to quickly extend a function by wrapping it in a another function. This is useful for a number of reasons – perhaps you want to occasionally log details about a function to file, or log the execution time of a function. You can add this code directly, but if you have several functions to add, it’s repetitive and annoying. Doing this with decorators saves you time and effort, and is easier to understand too!
Timer function
Note that their are two commonly used built in decorators, @classmethod
and @staticmethod
. I won’t really talk about them now, but this is a decent summary.
This is a decorator, called timer
. Inside, a wrapper, called f
, is defined. Inside f
, I start a timer (before = time.perf_counter()
), call the function I want to time (rv = func(*args, **kwargs)
), then print the time elapsed ( print(f"Time Elapsed for {func.__name__} is {after - before} seconds")
). That’s it.
The demo code:
import time
def timer(func):
def f(*args, **kwargs):
before = time.perf_counter()
rv = func(*args, **kwargs)
after = time.perf_counter()
print(f"Time Elapsed for {func.__name__} is {after - before} seconds")
return rv
return f
*args, **kwargs
allow me to pass thru arbitrary arguments to func from the wrapper, while func.__name__
accesses the function name using dunder magic.
To use this on a function like
def printHello(myname):
print(f"Hello {myname}!")
all that is required is
@timer
def printHello(myname):
print(f"Hello {myname}!")
Now something like
printHello("ethan")
returns
My name is ethan
Time Elapsed for printHello is 0.00038960000000000383 seconds