Around Python 2.2, before I had started to code in Python, PEP 252 and 253 paved the way for what I consider some of Python's most powerful features. There are a few issues I'd like to focus on the first of those is the ability subclass python's base types.
So say we need a dictionary that when a key is set to a value on that
dictionary the key is appended to a list instead of overriding the old value.
This is useful in for example HTTP headers where things like Set-Cookie will
appear multiple times but with different values. We have a few options to go
with here. We can roll our own class which implements the same methods as
dict and so would thus work just like a dict (aside from the changes we want).
So what exactly would have to be implemented to make that possible on a
reasonably usable level; we'll need: __getitem__, __repr__, __setitem__,
__delitem__, items, iteritems, values, itervalues, keys, iterkeys,
__contains__, and get. I'd say that would provide a reasonably usable and
introspectively capable implementation of dict which we could adapt for our
purposes. That's a lot of code that acts just like dict. So we have another
option. We can use UserDict which is basically dict reimplemented so it can be
inherited from which is back from the days before the great unification. So
that brings us to the awesome approach that is provided by the grand
unification. We can just inherit from dict and override the parts we need to
change. Our example can look something like:
class ResponseHeaders(dict):
def __setitem__(self, item, val):
if item in self:
iv = self[item]
if isinstance(iv, list):
iv.append(val)
else:
iv = [iv, val]
super(ResponseHeader, self).__setitem__(item, iv)
else:
super(ResponseHeader, self).__setitem__(item, val)
def items(self):
ret = []
for k,v in super(ResponseHeader, self).items():
if isinstance(v, list):
ret.extend([ (k, a) for a in v ])
else:
ret.append((k, v))
return ret
def iteritems(self):
return iter(self.items())
That's a really cool feature that makes my life a lot easier in that if I'm
using a dict to store things and I pretty much need a dict, I can adapt the
dict to be just right instead of writing a new kind of object. The great
unification had some other really cool side-effects though. It's now possible
to override __new__ which is the method used to actually create the object
instance in a class. A classic reason you might need this is for the
Singleton pattern. You should of
course remember the reasons not to use the Singleton. Anyway, it's good as an exercise to
show off new:
class Singleton(object):
_single = None
def __new__(typ, *args, *kwargs):
if not typ._single:
typ._single = object.__new__(typ, *args, **kwargs)
return typ._single
It might be better to directly call the object's __new__ method instead of
calling object's __new__ method indirectly, but the power of being able to
override __new__ is still quite clear.
I use this capability a lot. TagFu is built around overriding dict and list so that they read and write from a sqlite database instead of in memory storage. Combined with Python's expressiveness the capability of python's native types and the ease of overriding and adapting them are why I am such an avid Python programmer.