blob: 70fa90352d8604feaed0baa7af5d6ce16b976bbf [file] [log] [blame]
Guido van Rossumd8faa362007-04-27 19:54:29 +00001__all__ = ['deque', 'defaultdict', 'NamedTuple']
2
3from _collections import deque, defaultdict
4from operator import itemgetter as _itemgetter
5import sys as _sys
6
Guido van Rossumcd16bf62007-06-13 18:07:49 +00007# For bootstrapping reasons, the collection ABCs are defined in _abcoll.py.
8# They should however be considered an integral part of collections.py.
9from _abcoll import *
10import _abcoll
11__all__ += _abcoll.__all__
12
13
Thomas Wouters1b7f8912007-09-19 03:06:30 +000014def NamedTuple(typename, s, verbose=False):
Guido van Rossumd8faa362007-04-27 19:54:29 +000015 """Returns a new subclass of tuple with named fields.
16
17 >>> Point = NamedTuple('Point', 'x y')
Thomas Wouters1b7f8912007-09-19 03:06:30 +000018 >>> Point.__doc__ # docstring for the new class
Guido van Rossumd8faa362007-04-27 19:54:29 +000019 'Point(x, y)'
Thomas Wouters1b7f8912007-09-19 03:06:30 +000020 >>> p = Point(11, y=22) # instantiate with positional args or keywords
21 >>> p[0] + p[1] # works just like the tuple (11, 22)
Guido van Rossumd8faa362007-04-27 19:54:29 +000022 33
Thomas Wouters1b7f8912007-09-19 03:06:30 +000023 >>> x, y = p # unpacks just like a tuple
Guido van Rossumd8faa362007-04-27 19:54:29 +000024 >>> x, y
25 (11, 22)
Thomas Wouters1b7f8912007-09-19 03:06:30 +000026 >>> p.x + p.y # fields also accessable by name
Guido van Rossumd8faa362007-04-27 19:54:29 +000027 33
Thomas Wouters1b7f8912007-09-19 03:06:30 +000028 >>> p # readable __repr__ with name=value style
Guido van Rossumd8faa362007-04-27 19:54:29 +000029 Point(x=11, y=22)
Thomas Wouters1b7f8912007-09-19 03:06:30 +000030 >>> p.__replace__('x', 100) # __replace__() is like str.replace() but targets a named field
31 Point(x=100, y=22)
32 >>> d = dict(zip(p.__fields__, p)) # use __fields__ to make a dictionary
33 >>> d['x']
34 11
Guido van Rossumd8faa362007-04-27 19:54:29 +000035
36 """
37
Thomas Wouters1b7f8912007-09-19 03:06:30 +000038 field_names = tuple(s.replace(',', ' ').split()) # names separated by spaces and/or commas
39 if not ''.join((typename,) + field_names).replace('_', '').isalnum():
Guido van Rossumd59da4b2007-05-22 18:11:13 +000040 raise ValueError('Type names and field names can only contain alphanumeric characters and underscores')
Thomas Wouters1b7f8912007-09-19 03:06:30 +000041 argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
Guido van Rossumd59da4b2007-05-22 18:11:13 +000042 reprtxt = ', '.join('%s=%%r' % name for name in field_names)
43 template = '''class %(typename)s(tuple):
44 '%(typename)s(%(argtxt)s)'
45 __slots__ = ()
Thomas Wouters1b7f8912007-09-19 03:06:30 +000046 __fields__ = %(field_names)r
Guido van Rossumd59da4b2007-05-22 18:11:13 +000047 def __new__(cls, %(argtxt)s):
Thomas Wouters1b7f8912007-09-19 03:06:30 +000048 return tuple.__new__(cls, (%(argtxt)s))
Guido van Rossumd59da4b2007-05-22 18:11:13 +000049 def __repr__(self):
50 return '%(typename)s(%(reprtxt)s)' %% self
Thomas Wouters1b7f8912007-09-19 03:06:30 +000051 def __replace__(self, field, value):
52 'Return a new %(typename)s object replacing one field with a new value'
53 return %(typename)s(**dict(list(zip(%(field_names)r, self)) + [(field, value)])) \n''' % locals()
Guido van Rossumd59da4b2007-05-22 18:11:13 +000054 for i, name in enumerate(field_names):
Thomas Wouters1b7f8912007-09-19 03:06:30 +000055 template += ' %s = property(itemgetter(%d))\n' % (name, i)
56 if verbose:
57 print(template)
Guido van Rossumd59da4b2007-05-22 18:11:13 +000058 m = dict(itemgetter=_itemgetter)
59 exec(template, m)
60 result = m[typename]
61 if hasattr(_sys, '_getframe'):
62 result.__module__ = _sys._getframe(1).f_globals['__name__']
63 return result
Guido van Rossumd8faa362007-04-27 19:54:29 +000064
Guido van Rossumd8faa362007-04-27 19:54:29 +000065
Guido van Rossumd8faa362007-04-27 19:54:29 +000066
Guido van Rossumd8faa362007-04-27 19:54:29 +000067
68
69if __name__ == '__main__':
Thomas Wouters1b7f8912007-09-19 03:06:30 +000070 # verify that instances can be pickled
Guido van Rossum99603b02007-07-20 00:22:32 +000071 from pickle import loads, dumps
Thomas Wouters1b7f8912007-09-19 03:06:30 +000072 Point = NamedTuple('Point', 'x, y', True)
Guido van Rossumd8faa362007-04-27 19:54:29 +000073 p = Point(x=10, y=20)
74 assert p == loads(dumps(p))
75
76 import doctest
77 TestResults = NamedTuple('TestResults', 'failed attempted')
78 print(TestResults(*doctest.testmod()))