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[2]
    print emp2, emp2.first, emp2[3]

    pro1 = Product(['Box', .10, .05, 100])

    print pro1, pro1.profit, pro1[0]
