In recipe 303439 (http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303439) Andrew Durdin presents a customized tuple class that permits individual item access via a named attribute. I think this concept is quite novel and, as Andrew points out, can greatly improve readability of code. In this recipe I implement his tuple concept in a slightly different manner.
Instead of requiring the named attributes list for each instantiation of the tuple, my implementation 'Spawns' a derived tuple class that is taylored to the named attributes specified. And it is this Spawned class that is then used to instantiate tuples. My approach effectively separates the definition of the attribute names from the data specification. And in doing so, this approach diminishes instantiation hassles and further improves clarity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
class NamedTuple( tuple ): def __new__( cls, names ): return NamedTuple.Spawn( names ) def __getattr__( self, name ): try: return tuple.__getitem__( self, tuple.__getattribute__( self, '_names' )[name] ) except KeyError: raise AttributeError, "object has no attribute named '%s'" % name except AttributeError: raise RuntimeError, "'NamedTuple' class cannot be used directly, use subclass generated by Spawn instead" def __setattr__( self, name, value ): raise TypeError, "'NamedTuple' object has only read-only attributes (assign to .%s)" % name def Spawn( names ): class __temp( NamedTuple ): def __new__( cls, values ): return tuple.__new__( cls, values ) __temp._names = dict( zip( names, range( len( names ) ))) return __temp Spawn = staticmethod( Spawn ) if __name__ == '__main__': # There are two ways to Spawn a tuple class: Employee = NamedTuple.Spawn(['first', 'last', 'id']) Product = NamedTuple(['name', 'cost', 'profit', 'qty']) emp1 = Employee(['Jane', 'Foo', 1]) # The number of values is not limited to the number of # named attributes defined. emp2 = Employee(['John', 'Bar', 2, 'What about this?']) print emp1, emp1.last, emp1 print emp2, emp2.first, emp2 pro1 = Product(['Box', .10, .05, 100]) print pro1, pro1.profit, pro1
There are several things to notice here about using this modified NamedTuple class, and most notably is the clarity and ease of instantiation. Now the syntax looks more like the native tuple syntax, and requires less tricks like the zip function needed in the prior implementation.
The number of data values is not limited to the number of defined attributes, although it would be easy to enforce if desired.
There are two ways to Spawn an attributed tuple class. The only difference between the two is preference in readability.
With so many different ways to group data in Python using classes, dicts, lists, and tuples, I find Andrew's approach to be very fascinating. It affords the use of named arguments as a class or modified dict would, but yet retains the immutability of a tuple (among other tuple features). I certainly appreciate Andrew's contribution and hope my additions to his work will help others exploit this concept in their applications.