cpython/Lib/collections.py
Christian Heimes b9eccbfe2a Merged revisions 59333-59370 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r59343 | georg.brandl | 2007-12-05 08:02:47 +0100 (Wed, 05 Dec 2007) | 2 lines

  Fix typo.
........
  r59347 | christian.heimes | 2007-12-05 13:31:44 +0100 (Wed, 05 Dec 2007) | 1 line

  Fixed quoting and paths in the sqlite project file
........
  r59348 | christian.heimes | 2007-12-05 13:45:11 +0100 (Wed, 05 Dec 2007) | 1 line

  Fixed error in regrtest. I must have missed the spot.
........
  r59350 | christian.heimes | 2007-12-05 13:49:14 +0100 (Wed, 05 Dec 2007) | 1 line

  merge -r59315:59316 from py3k: Fix issue #1553: An errornous __length_hint__ can make list() raise a SystemError
........
  r59352 | christian.heimes | 2007-12-05 13:52:34 +0100 (Wed, 05 Dec 2007) | 1 line

  Added msg to Misc/NEWS
........
  r59354 | andrew.kuchling | 2007-12-05 14:27:20 +0100 (Wed, 05 Dec 2007) | 1 line

  Spelling fix
........
  r59356 | georg.brandl | 2007-12-05 18:56:50 +0100 (Wed, 05 Dec 2007) | 3 lines

  Add examples to csv, pprint and traceback docs.
  Written by Ross for GHOP.
........
  r59358 | raymond.hettinger | 2007-12-05 19:11:08 +0100 (Wed, 05 Dec 2007) | 1 line

  Error checking was too aggressive (reported by Chris Tismer)
........
  r59359 | georg.brandl | 2007-12-05 19:30:48 +0100 (Wed, 05 Dec 2007) | 2 lines

  Add examples to re docs. Written for GHOP by Dan Finnie.
........
  r59366 | georg.brandl | 2007-12-05 20:49:21 +0100 (Wed, 05 Dec 2007) | 2 lines

  Fix markup.
........
  r59367 | christian.heimes | 2007-12-05 20:57:54 +0100 (Wed, 05 Dec 2007) | 1 line

  Updated documentation and build_tkinter.py script
........
  r59368 | georg.brandl | 2007-12-05 21:03:57 +0100 (Wed, 05 Dec 2007) | 2 lines

  Another markup fix.
........
  r59369 | ronald.oussoren | 2007-12-05 21:07:36 +0100 (Wed, 05 Dec 2007) | 7 lines

  This "fixes" compilation issues for the Carbon._OSA module on OSX Leopard
  by purging bindings to OSA's debug API's. Those APIs we're completely
  unsupported on OSX 10.4 and are no longer available on OSX 10.5.

  Note that this patches a generated file. This is somewhat acceptable because
  regenerating the file is non-trivial and wouldn't use system headers anyway.
........
  r59370 | christian.heimes | 2007-12-05 21:10:38 +0100 (Wed, 05 Dec 2007) | 1 line

  Fixed bug #1557 by using popen.communicate() before popen.wait()
........
2007-12-05 20:18:38 +00:00

112 lines
4.5 KiB
Python

__all__ = ['deque', 'defaultdict', 'namedtuple']
from _collections import deque, defaultdict
from operator import itemgetter as _itemgetter
from keyword import iskeyword as _iskeyword
import sys as _sys
# 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__
def namedtuple(typename, field_names, verbose=False):
"""Returns a new subclass of tuple with named fields.
>>> Point = namedtuple('Point', 'x y')
>>> Point.__doc__ # docstring for the new class
'Point(x, y)'
>>> p = Point(11, y=22) # instantiate with positional args or keywords
>>> p[0] + p[1] # works just like the tuple (11, 22)
33
>>> x, y = p # unpacks just like a tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessable by name
33
>>> d = p.__asdict__() # convert to a dictionary
>>> d['x']
11
>>> Point(**d) # convert from a dictionary
Point(x=11, y=22)
>>> p.__replace__(x=100) # __replace__() is like str.replace() but targets named fields
Point(x=100, y=22)
"""
# Parse and validate the field names
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
field_names = tuple(field_names)
for name in (typename,) + field_names:
if not all(c.isalnum() or c=='_' for c in name):
raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
if _iskeyword(name):
raise ValueError('Type names and field names cannot be a keyword: %r' % name)
if name[0].isdigit():
raise ValueError('Type names and field names cannot start with a number: %r' % name)
seen_names = set()
for name in field_names:
if name.startswith('__') and name.endswith('__') and len(name) > 3:
raise ValueError('Field names cannot start and end with double underscores: %r' % name)
if name in seen_names:
raise ValueError('Encountered duplicate field name: %r' % name)
seen_names.add(name)
# Create and fill-in the class template
argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
reprtxt = ', '.join('%s=%%r' % name for name in field_names)
template = '''class %(typename)s(tuple):
'%(typename)s(%(argtxt)s)'
__slots__ = ()
__fields__ = property(lambda self: %(field_names)r)
def __new__(cls, %(argtxt)s):
return tuple.__new__(cls, (%(argtxt)s))
def __repr__(self):
return '%(typename)s(%(reprtxt)s)' %% self
def __asdict__(self, dict=dict, zip=zip):
'Return a new dict mapping field names to their values'
return dict(zip(%(field_names)r, self))
def __replace__(self, **kwds):
'Return a new %(typename)s object replacing specified fields with new values'
return %(typename)s(**dict(list(zip(%(field_names)r, self)) + list(kwds.items()))) \n''' % locals()
for i, name in enumerate(field_names):
template += ' %s = property(itemgetter(%d))\n' % (name, i)
if verbose:
print(template)
# Execute the template string in a temporary namespace
namespace = dict(itemgetter=_itemgetter)
try:
exec(template, namespace)
except SyntaxError as e:
raise SyntaxError(e.message + ':\n' + template)
result = namespace[typename]
# 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).
if hasattr(_sys, '_getframe'):
result.__module__ = _sys._getframe(1).f_globals['__name__']
return result
if __name__ == '__main__':
# verify that instances can be pickled
from pickle import loads, dumps
Point = namedtuple('Point', 'x, y', True)
p = Point(x=10, y=20)
assert p == loads(dumps(p))
# test and demonstrate ability to override methods
Point.__repr__ = lambda self: 'Point(%.3f, %.3f)' % self
print(p)
import doctest
TestResults = namedtuple('TestResults', 'failed attempted')
print(TestResults(*doctest.testmod()))