Python: constrained containers
As it happens, I was playing with the del.icio.us API today because of reasons which I shall hopefully elaborate upon in the coming few days. While cooking up some code to interact with the API, I saw the need to model a basic del.icio.us post
entity. For my needs, this entity wouldn’t have to be much more than a dumb dictionary with keys like url
, description
, etc. So I started out that way but pretty soon I thought, wouldn’t it be nice to be able to write post.url
instead of post['url']
? Further, wouldn’t it be nice to constrain the keys in the dictionary to those required and prevent typo errors such as post['descrption']
instead of post['description']
?
This seemed like the perfect opportunity to put into practice some of the stuff I’d learned about “new” style classes not too long ago. So I wrote the following class which implements the requirements laid out in the preceding paragraph. Since I’m just getting the hang of this stuff, please critique the code!
class Post(object): """Class to model a del.icio.us Post. It works like a struct/dict like object which limits keys to a retricted subset, i.e. those used in a del.icio.us post.""" __slots__ = ['description', 'url', 'extended', 'tags'] def __getitem__(self, key): if key in self.__slots__: return self.__getattribute__(key) else: raise KeyError def __setitem__(self, key, value): if key in self.__slots__: self.__setattr__(key, value) else: raise KeyError('Given key is not allowed in class %s'\ % self.__class__.__name__) def __contains__(self, key): try: self.__getitem__(key) except: return False return True # urllib.urlencode() just needs this beyond the basic stuff above def items(self): return [(k, self[k]) for k in self.__slots__ if k in self]
And here’s the usage of the class.
>>> p = Post() >>> p.url Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: url >>> p.url = 'http://google.com' >>> p.url 'http://google.com' >>> p['url'] 'http://google.com' >>> p['desc'] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "yummy.py", line 17, in __getitem__ raise KeyError KeyError >>> p['description'] = 'Google homepage' >>> p['description'] 'Google homepage' >>> p.items() [('description', 'Google homepage'), ('url', 'http://google.com')] >>> p.tags = 'search' >>> p.items() [('description', 'Google homepage'), ('url', 'http://google.com'), ('tags', 'search')] >>> 'extended' in p False >>> p.extended = 'homepage of the world' >>> p.items() [('description', 'Google homepage'), ('url', 'http://google.com'), ('extended', 'homepage of the world'), ('tags', 'search')] >>>
So, what am I doing wrong? :-)