diff -r 39f33c15243b Lib/collections/__init__.py --- a/Lib/collections/__init__.py Sat Oct 29 10:50:00 2016 +0300 +++ b/Lib/collections/__init__.py Tue Nov 08 14:30:50 2016 +0200 @@ -411,6 +411,7 @@ def namedtuple(typename, field_names, *, seen.add(name) # Fill-in the class template + arg_list = repr(tuple(field_names)).replace("'", "")[1:-1] class_definition = _class_template.format( typename = typename, field_names = tuple(field_names), @@ -424,9 +425,68 @@ def namedtuple(typename, field_names, *, # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] - namespace = dict(__name__='namedtuple_%s' % typename) - exec(class_definition, namespace) - result = namespace[typename] + #namespace = dict(__name__='namedtuple_%s' % typename) + #exec(class_definition, namespace) + #result = namespace[typename] + + class result(tuple): + __slots__ = () + + def __new__(_cls, *args, **kwds): + if len(args) + len(kwds) != len(_cls._fields): + raise TypeError('Expected %d arguments, got %d' % (len(_cls._fields), len(args) + len(kwds))) + try: + args += tuple(kwds.pop(_cls._fields[i]) for i in range(len(args), len(_cls._fields))) + except KeyError as err: + raise TypeError('Got unexpected field name: %r' % str(err)) + if kwds: + raise TypeError('Got unexpected field names: %r' % list(kwds)) + return tuple.__new__(_cls, args) + + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + result = new(cls, iterable) + if len(result) != len(cls._fields): + raise TypeError('Expected %d arguments, got %d' % (len(cls._fields), len(result))) + return result + + def _replace(_self, **kwds): + result = _self._make(map(kwds.pop, _self._fields, _self)) + if kwds: + raise ValueError('Got unexpected field names: %r' % list(kwds)) + return result + + def __repr__(self): + 'Return a nicely formatted representation string' + repr_fmt = ', '.join(f'{name}=%r' for name in self._fields) + return '%s(%s)' % (self.__class__.__name__, repr_fmt % self) + + def _asdict(self): + 'Return a new OrderedDict which maps field names to their values.' + return OrderedDict(zip(self._fields, self)) + + def __getnewargs__(self): + 'Return self as a plain tuple. Used by copy and pickle.' + return tuple(self) + + result.__name__ = typename + result.__qualname__ = typename + result.__doc__ = f'{typename}({arg_list})' + result._fields = tuple(field_names) + #result.__new__ = eval(f'lambda _cls, {arg_list}: _tuple.__new__(_cls, ({arg_list}))', {'_tuple': tuple}) + result.__new__.__doc__ = f'Create new instance of {typename}({arg_list})' + def _make(cls, iterable, new=tuple.__new__, len=len): + result = new(cls, iterable) + if len(result) != len(cls._fields): + raise TypeError('Expected %d arguments, got %d' % (len(cls._fields), len(result))) + return result + _make.__doc__ = f'Make a new {typename} object from a sequence or iterable' + result._make = classmethod(_make) + #result._make.__doc__ = f'Make a new {typename} object from a sequence or iterable' + result._replace.__doc__ = f'Return a new {typename} object replacing specified fields with new values' + for index, name in enumerate(field_names): + setattr(result, name, property(_itemgetter(index), doc=f'Alias for field number {index:d}')) + result._source = class_definition if verbose: print(result._source)