Skip to content

Decorating Your Optional Arguments

February 1, 2013

For the first time in the (short) history of this blog, I’m going to post some actual useful code. The code this is based from is in my Git Hub under KDE-for-SciPy.

And now, on to the main point. I’m developing some kernel density estimation code for SciPy and/or statsmodels. For that, I need some test routines that amount to sampling from a few different probability distributions. Each distribution sampler is written up as its own function but has exactly the same arguments. Since I am a good Pythonic citizen, all of the defaults are set as nsamp=None and if nsamp is None: .... However, after about the fifth time I typed if nsamp is None:, I realized there has to be a better way to do this. In fact, you can use decorators to modify each function in a systematic way.

A quick overview of decorators. Decorators are functions of functions that allow you to manipulate functions in place, so to speak. Generally they look something like this:

def decorator(func):
  do something...
  def inner(options):
    return func(options)
  return inner

def function_to_decorate(options):
  do something entirely different
  return something

Your decorator takes a function as an argument and uses an ‘inner’ function to add some additional functionality to your function. Recall that in Python func is your function object, which can be passed around like any other object, and func() executes your function. One of my favorite decorators is a timing decorator:

from time import clock

def timing(func):
  start = clock()
  def inner(func):
    out = func(*args, **kwargs)
    print 'Function took: ', str(clock()-start), 'seconds'
    return out
  return inner

That decorator is not as useful as using %timeit from IPython, but there are cases where you have functions that can’t be subdivided to run a bunch of times. You can then use the timing decorator and get a (less accurate) estimate from running the function once.

Now that you have some idea what a decorator is, here is our optional arguments decorator. I’m going to use the wrapper I wrote for NumPy’s log normal sampler as a simple example:

def nsamp_opt(func):
  def inner(nsamp=None):
    if nsamp is None:
      nsamp = 1
    return func(nsamp)
  return inner

def log_normal(nsamp):
    return rand.lognormal(size=nsamp)

Admittedly, that only saves me one line of code per function, but the space saving ended up adding up since I defined 16 functions in the same way. For me, the biggest advantage is that all those lines of code saved means fewer chances for me to have typos to debug. Next time, I will show how to do the same thing in an object-oriented way. Also, I haven’t forgotten about the quaternion project and will return to that soon.


From → Python tricks

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: