python decorators
- 2 minutes read - 381 wordsI came across something that I think is actually pretty neat. Python supports ``decorating'' functions, which lets you define behaviors that essentially wrap around a given function. The benefit is that you can write a simple function and decorate it to do something fancier.
I realized I wanted to do this with a simple timing output. Say my function was just something really simple like
def f_plain(x,y):
print("x + y = {}".format( x+y))
return x+y
print ("---")
print (f_plain(1,3))
Now, to time it, I could define a function like:
def f_plain_timed(x, y):
import time
start = time.time()
retval = f_plain(x, y)
end = time.time()
print("Time elapsed in f_plain: {} seconds".format(end-start))
return retval
print ("---")
print (f_plain_timed(1,3))
But this isn’t a very general way to deal with it–I’d have to redefine f_plain_timed for each function I wanted to time. So instead, I can define a function that takes a function as an argument. There’s a bunch of steps here that I should be showing, and am not, but the idea is to get something like this:
def decor(g):
def decor_inner(*args, **kwargs):
import time
start = time.time()
retval = g(*args, **kwargs)
end = time.time()
print("Time elapsed in {}: {} sec".format(g.__name__, end-start))
return retval
return decor_inner
@decor
def f_decor(x,y):
print("x + y = {}".format(x+y))
return x+y
print ("---")
print (f_decor(1,3))
What’s going on here? Several things:
- We define a decorator
decor
- Define a decorator inner
decor_inner
which calls the function we passed originally - Python allows the syntactic sugar
@decor
to mark thatf_decor
should actually be the composition of the functiondecor
and the functionf_decor
as written. It returns a function with the call signature defined byf_decor
.
Finally–our decorators can actually even take parameters. This might be useful for specifying what logfiles a particular function should use. It’s also used in flask to define routes for applications, among other things. To define this kind of decorator, we need to wrap one more function:
def decor_msg(message):
def decor_msg_real(g):
def decor_msg_real_inner(*args, **kwargs):
import time
start = time.time()
retval = g(*args, **kwargs)
end = time.time()
print(message+" Time elapsed in {}: {} sec".format(g.__name__, end-start))
return decor_msg_real_inner
return decor_msg_real
m = "Hello!!!"
@decor_msg(m)
def f_decor_msg(x,y):
print("x + y = {}".format( x+y))
return x+y
print ("---")
print (f_decor(1,3))
I think that about ties it up. Back to work!