Recipe6.7.Implementing Tuples with Named Items


Recipe 6.7. Implementing Tuples with Named Items

Credit: Gonçalo Rodrigues, Raymond Hettinger

Problem

Python tuples are handy ways to group pieces of information, but having to access each item by numeric index is a bother. You'd like to build tuples whose items are also accessible as named attributes.

Solution

A factory function is the simplest way to generate the required subclass of tuple:

# use operator.itemgetter if we're in 2.4, roll our own if we're in 2.3 try:     from operator import itemgetter except ImportError:     def itemgetter(i):         def getter(self): return self[i]         return getter def superTuple(typename, *attribute_names):     " create and return a subclass of `tuple', with named attributes "     # make the subclass with appropriate _ _new_ _ and _ _repr_ _ specials     nargs = len(attribute_names)     class supertup(tuple):         _ _slots_ _ = ( )         # save memory, we don't need per-instance dict         def _ _new_ _(cls, *args):             if len(args) != nargs:                 raise TypeError, '%s takes exactly %d arguments (%d given)' % (                                   typename, nargs, len(args))             return tuple._ _new_ _(cls, args)         def _ _repr_ _(self):             return '%s(%s)' % (typename, ', '.join(map(repr, self)))     # add a few key touches to our new subclass of `tuple'     for index, attr_name in enumerate(attribute_names):         setattr(supertup, attr_name, property(itemgetter(index)))     supertup._ _name_ _ = typename     return supertup

Discussion

You often want to pass data around by means of tuples, which play the role of C's structs, or that of simple records in other languages. Having to remember which numeric index corresponds to which field, and accessing the fields by indexing, is often bothersome. Some Python Standard Library modules, such as time and os, which in old Python versions used to return tuples, have fixed the problem by returning, instead, instances of tuple-like types that let you access the fields by name, as attributes, as well as by index, as items. This recipe shows you how to get the same effect for your code, essentially by automatically building a custom subclass of tuple.

Orchestrating the building of a new, customized type can be achieved in several ways; custom metaclasses are often the best approach for such tasks. In this case, however, a simple factory function is quite sufficient, and you should never use more power than you need. Here is how you can use this recipe's superTuple factory function in your code, assuming you have saved this recipe's Solution as a module named supertuple.py somewhere along your Python sys.path:

>>> import supertuple >>> Point = supertuple.superTuple('Point', 'x', 'y') >>> Point <class 'supertuple.Point'> >>> p = Point(1, 2, 3)              # wrong number of fields Traceback (most recent call last):   File "", line 1, in ?   File "C:\Python24\Lib\site-packages\superTuple.py", line 16, in _ _new_ _     raise TypeError, '%s takes exactly %d arguments (%d given)' % ( TypeError: Point takes exactly 2 arguments (3 given) >>> p = Point(1, 2)                 # let's do it right this time >>> p Point(1, 2) >>> print p.x, p.y 1 2

Function superTuple's implementation is quite straightforward. To build the new subclass, superTuple uses a class statement, and in that statement's body, it defines three specials: an "empty" _ _slots_ _ (just to save memory, since our supertuple instances don't need any per-instance dictionary anyway); a _ _new_ _ method that checks the number of arguments before delegating to tuple._ _new_ _; and an appropriate _ _repr_ _ method. After the new class object is built, we set into it a property for each named attribute we want. Each such property has only a "getter", since our supertuples, just like tuples themselves, are immutableno setting of fields. Finally, we set the new class' name and return the class object.

Each of the getters is easily built by a simple call to the built-in itemgetter from the standard library module operator. Since operator.itemgetter was introduced in Python 2.4, at the very start of our module we ensure we have a suitable itemgetter at hand anyway, even in Python 2.3, by rolling our own if necessary.

See Also

Library Reference and Python in a Nutshell docs for property, _ _slots_ _, tuple, and special methods _ _new_ _ and _ _repr_ _; (Python 2.4 only) module operator's function itemgetter.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net