2010-06-13

Convenient improvised classes in Python

When writing a test suite for a little project I'm working on (multi-part blog post forthcoming), I wanted to return an array of objects from a function. Since they were only going to hold some values that would be passed to another function and then forgotten, I didn't feel like writing a proper class for them. The standard way would be to return a tuple or a dictionary, but I prefer the attribute access notation (foo.bar) to indexing (foo["bar"]), especially when sending the object to some other function that doesn't really care to keep track of what's a normal object and what just happens to be a dictionary because I was lazy.

So, what can be done? Turns you you can both have your cake and eat it too.


class improvise(object):
def __init__(self, **kws):
for (name, value) in kws.items():
setattr(self, name, value)

This little class takes keyword arguments to its constructor, and assigns each key/value pair as attributes on the instance. Use it like this: improvise(from_user=from_user, subject=subject, query=query), and the caller then gets an object that can be used like this: message.subject. Since the class doesn't care about which keyword arguments are used, it can be re-used by every function that wants to return objects without having to declare classes for them.

Chalk up one more for the flexibility of Python. Which, by the way, zooms.

3 comments:

  1. It's of course also possible to write it (you'll have to imagine the indentation) as


    class improvise(object):
    def __init__(self, **kws):
    self.dict=kws
    def __getattr__(self, attr):
    return self.dict[attr]


    which I guess is less odd, but on the other hand does not support the dir() function.

    ReplyDelete
  2. The obvious solution to all this is of course (again with the imagined indentation):
    class improvise(object):
    def __init__(self, **kws):
    self.__dict__.update(kws)
    KInd of embarrassing I didn't think of this in the first place...

    ReplyDelete
  3. But then again, if you're on Python 2.6 or later you should just use namedtuple:
    http://docs.python.org/library/collections.html#collections.namedtuple

    ReplyDelete