Skip to content

Calling Your Objects

February 8, 2013

As I mentioned last time, I am not a fan of repeatedly typing if X is None: over and over. One solution to that is to use decorators to turn ordered options into keyword arguments. The solution I’m going to write about today is using classes. The first example will be using a instance method which takes keyword arguments to wrap other methods with ordered arguments. In the second example, I’m going to create a simple linear congruence random number generator and make instances of our class into a function.

I’m sure I have seen this idea a number of times, but I first remember seeing a class with an outward instance method wrapping another method in scipy.stats.rv_continuous. That class exists solely to be subclassed generating new continuous random variables. As a simple example, here is a class with method func:

class MyClass(object):
  def func(Nsamp=None):
    if Nsamp is None:
      Nsamp = 1000
    return _func(Nsamp)
  def _func(nsamp):
    pass

That allows us to write a short subclass:

class MySubClass(MyClass):
def _func(nsamp):
return np.random.randn(size=nsamp)

That’s basically the same behavior as the decorator but for instance methods.

I included the above for completeness, but we should write some code using something new and fun. In fact, we’re going to use __call__ to make instances classes behave similarly to functions. And, I’m actually going to use it for something useful! In general, you probably wouldn’t want to write a random number generator in pure Python. It’s just not that wise. However, random number generators are a great example of a process you want to change the state of on the fly. Let’s define our update function real quick:

def _draw(seed, a, b, m):
  return (a*seed + b) % m

There isn’t anything really special there, just a linear congruence random number generator. Alright, now let’s write our class:

class MyRand(object):
  def __init__(self, seed, a, b, m):
    self.seed = seed
    self.a = a
    self.b = b
    self.m = m
  def __call__(self):
    seed = _draw(self.seed, self.a, self.b, self.m)
    return float(seed)/float(m)

Let’s walk through that class. We initialize our object in the usual way, and then we add a __call__ method. Here’s how we would use this code:

generator = MyRand(seed, i, j, k)
r = generator()
"""
do more things
"""
# reset the seed
generator.seed = seed
r = generator() # generates the same number as in line 2

When we call the instantiation generator of our class MyRand it calls our __call__ function, generates a new random number and updates the seed. We could use a generator to do something similar, but we wouldn’t be able to change things mid-stream quite as well. For reference, here is the equivalent generator code:

def generate(seed, a, b, m):
  while True:
    seed = _draw(seed, a, b, m)
    yield seed/m

In order to change any of the parameters of our generator, we need to remember a, b, and m and infer seed before making our change and creating a new generator. I think the callable object is a nice solution to that problem.

Advertisements

From → Python tricks

Leave a Comment

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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: