| 
									
										
										
										
											2007-10-23 20:37:41 +00:00
										 |  |  | __all__ = ['deque', 'defaultdict', 'namedtuple'] | 
					
						
							| 
									
										
										
										
											2007-12-18 00:13:45 +00:00
										 |  |  | # For bootstrapping reasons, the collection ABCs are defined in _abcoll.py. | 
					
						
							|  |  |  | # They should however be considered an integral part of collections.py. | 
					
						
							|  |  |  | from _abcoll import * | 
					
						
							|  |  |  | import _abcoll | 
					
						
							|  |  |  | __all__ += _abcoll.__all__ | 
					
						
							| 
									
										
										
										
											2007-02-28 18:37:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | from _collections import deque, defaultdict | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  | from operator import itemgetter as _itemgetter | 
					
						
							| 
									
										
										
										
											2007-10-16 21:28:32 +00:00
										 |  |  | from keyword import iskeyword as _iskeyword | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  | import sys as _sys | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-23 20:37:41 +00:00
										 |  |  | def namedtuple(typename, field_names, verbose=False): | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     """Returns a new subclass of tuple with named fields.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-23 20:37:41 +00:00
										 |  |  |     >>> Point = namedtuple('Point', 'x y') | 
					
						
							| 
									
										
										
										
											2007-09-17 00:55:00 +00:00
										 |  |  |     >>> Point.__doc__                   # docstring for the new class | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     'Point(x, y)' | 
					
						
							| 
									
										
										
										
											2007-09-17 00:55:00 +00:00
										 |  |  |     >>> p = Point(11, y=22)             # instantiate with positional args or keywords | 
					
						
							| 
									
										
										
										
											2007-12-18 22:21:27 +00:00
										 |  |  |     >>> p[0] + p[1]                     # indexable like a plain tuple | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     33 | 
					
						
							| 
									
										
										
										
											2007-12-18 00:13:45 +00:00
										 |  |  |     >>> x, y = p                        # unpack like a regular tuple | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     >>> x, y | 
					
						
							|  |  |  |     (11, 22) | 
					
						
							| 
									
										
										
										
											2007-09-17 00:55:00 +00:00
										 |  |  |     >>> p.x + p.y                       # fields also accessable by name | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     33 | 
					
						
							| 
									
										
										
										
											2007-12-14 02:49:47 +00:00
										 |  |  |     >>> d = p._asdict()                 # convert to a dictionary | 
					
						
							| 
									
										
										
										
											2007-10-05 02:47:07 +00:00
										 |  |  |     >>> d['x'] | 
					
						
							|  |  |  |     11 | 
					
						
							|  |  |  |     >>> Point(**d)                      # convert from a dictionary | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     Point(x=11, y=22) | 
					
						
							| 
									
										
										
										
											2007-12-14 02:49:47 +00:00
										 |  |  |     >>> p._replace(x=100)               # _replace() is like str.replace() but targets named fields | 
					
						
							| 
									
										
										
										
											2007-09-17 00:55:00 +00:00
										 |  |  |     Point(x=100, y=22) | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-08 02:02:05 +00:00
										 |  |  |     # Parse and validate the field names.  Validation serves two purposes, | 
					
						
							|  |  |  |     # generating informative error messages and preventing template injection attacks. | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  |     if isinstance(field_names, basestring): | 
					
						
							| 
									
										
										
										
											2007-10-08 10:11:51 +00:00
										 |  |  |         field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  |     field_names = tuple(field_names) | 
					
						
							| 
									
										
										
										
											2007-10-16 19:18:30 +00:00
										 |  |  |     for name in (typename,) + field_names: | 
					
						
							| 
									
										
										
										
											2007-12-05 18:11:08 +00:00
										 |  |  |         if not all(c.isalnum() or c=='_' for c in name): | 
					
						
							| 
									
										
										
										
											2007-10-16 19:18:30 +00:00
										 |  |  |             raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) | 
					
						
							| 
									
										
										
										
											2007-10-16 21:28:32 +00:00
										 |  |  |         if _iskeyword(name): | 
					
						
							|  |  |  |             raise ValueError('Type names and field names cannot be a keyword: %r' % name) | 
					
						
							| 
									
										
										
										
											2007-10-16 19:18:30 +00:00
										 |  |  |         if name[0].isdigit(): | 
					
						
							|  |  |  |             raise ValueError('Type names and field names cannot start with a number: %r' % name) | 
					
						
							| 
									
										
										
										
											2007-10-09 01:36:23 +00:00
										 |  |  |     seen_names = set() | 
					
						
							|  |  |  |     for name in field_names: | 
					
						
							| 
									
										
										
										
											2007-12-14 02:49:47 +00:00
										 |  |  |         if name.startswith('_'): | 
					
						
							|  |  |  |             raise ValueError('Field names cannot start with an underscore: %r' % name) | 
					
						
							| 
									
										
										
										
											2007-10-09 01:36:23 +00:00
										 |  |  |         if name in seen_names: | 
					
						
							| 
									
										
										
										
											2007-10-16 19:18:30 +00:00
										 |  |  |             raise ValueError('Encountered duplicate field name: %r' % name) | 
					
						
							| 
									
										
										
										
											2007-10-09 01:36:23 +00:00
										 |  |  |         seen_names.add(name) | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Create and fill-in the class template | 
					
						
							| 
									
										
										
										
											2008-01-05 01:35:43 +00:00
										 |  |  |     numfields = len(field_names) | 
					
						
							| 
									
										
										
										
											2007-09-18 03:33:19 +00:00
										 |  |  |     argtxt = repr(field_names).replace("'", "")[1:-1]   # tuple repr without parens or quotes | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |     reprtxt = ', '.join('%s=%%r' % name for name in field_names) | 
					
						
							| 
									
										
										
										
											2007-12-18 22:21:27 +00:00
										 |  |  |     dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |     template = '''class %(typename)s(tuple):
 | 
					
						
							| 
									
										
										
										
											2007-12-14 18:08:20 +00:00
										 |  |  |         '%(typename)s(%(argtxt)s)' \n | 
					
						
							|  |  |  |         __slots__ = () \n | 
					
						
							| 
									
										
										
										
											2008-01-04 03:22:53 +00:00
										 |  |  |         _fields = %(field_names)r \n | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |         def __new__(cls, %(argtxt)s): | 
					
						
							| 
									
										
										
										
											2007-12-14 18:08:20 +00:00
										 |  |  |             return tuple.__new__(cls, (%(argtxt)s)) \n | 
					
						
							| 
									
										
										
										
											2008-01-05 01:35:43 +00:00
										 |  |  |         @classmethod | 
					
						
							| 
									
										
										
										
											2008-01-06 22:11:54 +00:00
										 |  |  |         def _make(cls, iterable, new=tuple.__new__, len=len): | 
					
						
							| 
									
										
										
										
											2008-01-05 01:35:43 +00:00
										 |  |  |             'Make a new %(typename)s object from a sequence or iterable' | 
					
						
							| 
									
										
										
										
											2008-01-06 22:11:54 +00:00
										 |  |  |             result = new(cls, iterable) | 
					
						
							| 
									
										
										
										
											2008-01-05 01:35:43 +00:00
										 |  |  |             if len(result) != %(numfields)d: | 
					
						
							|  |  |  |                 raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) | 
					
						
							|  |  |  |             return result \n | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |         def __repr__(self): | 
					
						
							| 
									
										
										
										
											2007-12-14 18:08:20 +00:00
										 |  |  |             return '%(typename)s(%(reprtxt)s)' %% self \n | 
					
						
							| 
									
										
										
										
											2007-12-18 22:21:27 +00:00
										 |  |  |         def _asdict(t): | 
					
						
							| 
									
										
										
										
											2007-12-14 18:08:20 +00:00
										 |  |  |             'Return a new dict which maps field names to their values' | 
					
						
							| 
									
										
										
										
											2007-12-18 22:21:27 +00:00
										 |  |  |             return {%(dicttxt)s} \n | 
					
						
							| 
									
										
										
										
											2007-12-14 02:49:47 +00:00
										 |  |  |         def _replace(self, **kwds): | 
					
						
							| 
									
										
										
										
											2007-11-15 02:44:53 +00:00
										 |  |  |             'Return a new %(typename)s object replacing specified fields with new values' | 
					
						
							| 
									
										
										
										
											2008-01-06 09:02:24 +00:00
										 |  |  |             result = self._make(map(kwds.pop, %(field_names)r, self)) | 
					
						
							| 
									
										
										
										
											2008-01-05 02:17:24 +00:00
										 |  |  |             if kwds: | 
					
						
							|  |  |  |                 raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) | 
					
						
							|  |  |  |             return result \n\n''' % locals()
 | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |     for i, name in enumerate(field_names): | 
					
						
							| 
									
										
										
										
											2007-09-18 03:33:19 +00:00
										 |  |  |         template += '        %s = property(itemgetter(%d))\n' % (name, i) | 
					
						
							|  |  |  |     if verbose: | 
					
						
							|  |  |  |         print template | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Execute the template string in a temporary namespace | 
					
						
							| 
									
										
										
										
											2007-12-18 22:21:27 +00:00
										 |  |  |     namespace = dict(itemgetter=_itemgetter) | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2007-10-08 10:11:51 +00:00
										 |  |  |         exec template in namespace | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  |     except SyntaxError, e: | 
					
						
							|  |  |  |         raise SyntaxError(e.message + ':\n' + template) | 
					
						
							| 
									
										
										
										
											2007-10-08 10:11:51 +00:00
										 |  |  |     result = namespace[typename] | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # For pickling to work, the __module__ variable needs to be set to the frame | 
					
						
							|  |  |  |     # where the named tuple is created.  Bypass this step in enviroments where | 
					
						
							|  |  |  |     # sys._getframe is not defined (Jython for example). | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |     if hasattr(_sys, '_getframe'): | 
					
						
							|  |  |  |         result.__module__ = _sys._getframe(1).f_globals['__name__'] | 
					
						
							| 
									
										
										
										
											2007-10-08 09:14:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-19 01:11:16 +00:00
										 |  |  |     return result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2007-09-17 00:55:00 +00:00
										 |  |  |     # verify that instances can be pickled | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     from cPickle import loads, dumps | 
					
						
							| 
									
										
										
										
											2007-10-23 20:37:41 +00:00
										 |  |  |     Point = namedtuple('Point', 'x, y', True) | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     p = Point(x=10, y=20) | 
					
						
							|  |  |  |     assert p == loads(dumps(p)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-15 02:44:53 +00:00
										 |  |  |     # test and demonstrate ability to override methods | 
					
						
							| 
									
										
										
										
											2008-01-07 04:24:49 +00:00
										 |  |  |     class Point(namedtuple('Point', 'x y')): | 
					
						
							| 
									
										
										
										
											2008-01-10 19:15:10 +00:00
										 |  |  |         __slots__ = () | 
					
						
							| 
									
										
										
										
											2008-01-07 04:24:49 +00:00
										 |  |  |         @property | 
					
						
							|  |  |  |         def hypot(self): | 
					
						
							|  |  |  |             return (self.x ** 2 + self.y ** 2) ** 0.5 | 
					
						
							| 
									
										
										
										
											2008-01-07 20:07:38 +00:00
										 |  |  |         def __str__(self): | 
					
						
							|  |  |  |             return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) | 
					
						
							| 
									
										
										
										
											2008-01-07 04:24:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-10 19:15:10 +00:00
										 |  |  |     for p in Point(3, 4), Point(14, 5/7.): | 
					
						
							| 
									
										
										
										
											2008-01-07 20:07:38 +00:00
										 |  |  |         print p | 
					
						
							| 
									
										
										
										
											2007-11-15 02:44:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-07 09:03:49 +00:00
										 |  |  |     class Point(namedtuple('Point', 'x y')): | 
					
						
							|  |  |  |         'Point class with optimized _make() and _replace() without error-checking' | 
					
						
							| 
									
										
										
										
											2008-01-10 19:15:10 +00:00
										 |  |  |         __slots__ = () | 
					
						
							| 
									
										
										
										
											2008-01-07 09:03:49 +00:00
										 |  |  |         _make = classmethod(tuple.__new__) | 
					
						
							|  |  |  |         def _replace(self, _map=map, **kwds): | 
					
						
							| 
									
										
										
										
											2008-01-07 20:56:05 +00:00
										 |  |  |             return self._make(_map(kwds.get, ('x', 'y'), self)) | 
					
						
							| 
									
										
										
										
											2008-01-07 09:03:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print Point(11, 22)._replace(x=100) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     import doctest | 
					
						
							| 
									
										
										
										
											2007-10-23 20:37:41 +00:00
										 |  |  |     TestResults = namedtuple('TestResults', 'failed attempted') | 
					
						
							| 
									
										
										
										
											2007-03-01 06:16:43 +00:00
										 |  |  |     print TestResults(*doctest.testmod()) |