blob: fa9a9a0f8a953a0b8991658b29f23e7d00609908 [file] [log] [blame]
Raymond Hettingerc37e5e02007-03-01 06:16:43 +00001__all__ = ['deque', 'defaultdict', 'NamedTuple']
Raymond Hettingereb979882007-02-28 18:37:52 +00002
3from _collections import deque, defaultdict
Raymond Hettingerc37e5e02007-03-01 06:16:43 +00004from operator import itemgetter as _itemgetter
5import sys as _sys
6
7def NamedTuple(typename, s):
8 """Returns a new subclass of tuple with named fields.
9
10 >>> Point = NamedTuple('Point', 'x y')
11 >>> Point.__doc__ # docstring for the new class
12 'Point(x, y)'
13 >>> p = Point(11, y=22) # instantiate with positional args or keywords
14 >>> p[0] + p[1] # works just like the tuple (11, 22)
15 33
16 >>> x, y = p # unpacks just like a tuple
17 >>> x, y
18 (11, 22)
19 >>> p.x + p.y # fields also accessable by name
20 33
21 >>> p # readable __repr__ with name=value style
22 Point(x=11, y=22)
23
24 """
25
26 field_names = s.split()
Raymond Hettinger5a41daf2007-05-19 01:11:16 +000027 assert ''.join(field_names).replace('_', '').isalpha() # protect against exec attacks
28 argtxt = ', '.join(field_names)
29 reprtxt = ', '.join('%s=%%r' % name for name in field_names)
30 template = '''class %(typename)s(tuple):
31 '%(typename)s(%(argtxt)s)'
32 __slots__ = ()
33 def __new__(cls, %(argtxt)s):
34 return tuple.__new__(cls, (%(argtxt)s,))
35 def __repr__(self):
36 return '%(typename)s(%(reprtxt)s)' %% self
37 ''' % locals()
38 for i, name in enumerate(field_names):
Raymond Hettingerf3241a32007-05-19 01:50:11 +000039 template += '\n %s = property(itemgetter(%d))\n' % (name, i)
Raymond Hettinger5a41daf2007-05-19 01:11:16 +000040 m = dict(itemgetter=_itemgetter)
41 exec template in m
42 result = m[typename]
43 if hasattr(_sys, '_getframe'):
44 result.__module__ = _sys._getframe(1).f_globals['__name__']
45 return result
Raymond Hettingerc37e5e02007-03-01 06:16:43 +000046
Raymond Hettingerc37e5e02007-03-01 06:16:43 +000047
Raymond Hettingerc37e5e02007-03-01 06:16:43 +000048
Raymond Hettingerc37e5e02007-03-01 06:16:43 +000049
Raymond Hettingerc37e5e02007-03-01 06:16:43 +000050
51
52if __name__ == '__main__':
53 # verify that instances are pickable
54 from cPickle import loads, dumps
55 Point = NamedTuple('Point', 'x y')
56 p = Point(x=10, y=20)
57 assert p == loads(dumps(p))
58
59 import doctest
60 TestResults = NamedTuple('TestResults', 'failed attempted')
61 print TestResults(*doctest.testmod())