summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2007-05-19 01:11:16 (GMT)
committerRaymond Hettinger <python@rcn.com>2007-05-19 01:11:16 (GMT)
commit5a41daf096d7fe7cb8d7624640cdc6bec74003c1 (patch)
treee66587812905fb2bb86996079ce8a3b343734c7e
parent6290305e6722c20e17d3c73bd6a30c6448bead83 (diff)
downloadcpython-5a41daf096d7fe7cb8d7624640cdc6bec74003c1.zip
cpython-5a41daf096d7fe7cb8d7624640cdc6bec74003c1.tar.gz
cpython-5a41daf096d7fe7cb8d7624640cdc6bec74003c1.tar.bz2
Improvements to NamedTuple's implementation, tests, and documentation
-rw-r--r--Doc/lib/libcollections.tex19
-rw-r--r--Lib/collections.py47
-rw-r--r--Lib/test/test_collections.py3
3 files changed, 40 insertions, 29 deletions
diff --git a/Doc/lib/libcollections.tex b/Doc/lib/libcollections.tex
index 443a7a4..c0eb6ec 100644
--- a/Doc/lib/libcollections.tex
+++ b/Doc/lib/libcollections.tex
@@ -378,14 +378,25 @@ Point(x=11, y=22)
The use cases are the same as those for tuples. The named factories
assign meaning to each tuple position and allow for more readable,
self-documenting code. Named tuples can also be used to assign field names
- to tuples
- returned by the \module{csv} or \module{sqlite3} modules. For example:
+ to tuples returned by the \module{csv} or \module{sqlite3} modules.
+ For example:
\begin{verbatim}
+from itertools import starmap
import csv
EmployeeRecord = NamedTuple('EmployeeRecord', 'name age title department paygrade')
-for tup in csv.reader(open("employees.csv", "rb")):
- print EmployeeRecord(*tup)
+for record in starmap(EmployeeRecord, csv.reader(open("employees.csv", "rb"))):
+ print record
+\end{verbatim}
+
+ To cast an individual record stored as \class{list}, \class{tuple}, or some other
+ iterable type, use the star-operator to unpack the values:
+
+ \begin{verbatim}
+>>> Color = NamedTuple('Color', 'name code')
+>>> m = dict(red=1, green=2, blue=3)
+>>> print Color(*m.popitem())
+Color(name='blue', code=3)
\end{verbatim}
\end{funcdesc}
diff --git a/Lib/collections.py b/Lib/collections.py
index 6e816dc..adba81c 100644
--- a/Lib/collections.py
+++ b/Lib/collections.py
@@ -24,30 +24,29 @@ def NamedTuple(typename, s):
"""
field_names = s.split()
- nargs = len(field_names)
-
- def __new__(cls, *args, **kwds):
- if kwds:
- try:
- args += tuple(kwds[name] for name in field_names[len(args):])
- except KeyError, name:
- raise TypeError('%s missing required argument: %s' % (typename, name))
- if len(args) != nargs:
- raise TypeError('%s takes exactly %d arguments (%d given)' % (typename, nargs, len(args)))
- return tuple.__new__(cls, args)
-
- repr_template = '%s(%s)' % (typename, ', '.join('%s=%%r' % name for name in field_names))
-
- m = dict(vars(tuple)) # pre-lookup superclass methods (for faster lookup)
- m.update(__doc__= '%s(%s)' % (typename, ', '.join(field_names)),
- __slots__ = (), # no per-instance dict (so instances are same size as tuples)
- __new__ = __new__,
- __repr__ = lambda self, _format=repr_template.__mod__: _format(self),
- __module__ = _sys._getframe(1).f_globals['__name__'],
- )
- m.update((name, property(_itemgetter(index))) for index, name in enumerate(field_names))
-
- return type(typename, (tuple,), m)
+ assert ''.join(field_names).replace('_', '').isalpha() # protect against exec attacks
+ argtxt = ', '.join(field_names)
+ reprtxt = ', '.join('%s=%%r' % name for name in field_names)
+ template = '''class %(typename)s(tuple):
+ '%(typename)s(%(argtxt)s)'
+ __slots__ = ()
+ def __new__(cls, %(argtxt)s):
+ return tuple.__new__(cls, (%(argtxt)s,))
+ def __repr__(self):
+ return '%(typename)s(%(reprtxt)s)' %% self
+ ''' % locals()
+ for i, name in enumerate(field_names):
+ template += '\t%s = property(itemgetter(%d))\n' % (name, i)
+ m = dict(itemgetter=_itemgetter)
+ exec template in m
+ result = m[typename]
+ if hasattr(_sys, '_getframe'):
+ result.__module__ = _sys._getframe(1).f_globals['__name__']
+ return result
+
+
+
+
if __name__ == '__main__':
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index a139129..b3f460e 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -11,7 +11,6 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(Point.__slots__, ())
self.assertEqual(Point.__module__, __name__)
self.assertEqual(Point.__getitem__, tuple.__getitem__)
- self.assert_('__getitem__' in Point.__dict__) # superclass methods localized
def test_instance(self):
Point = NamedTuple('Point', 'x y')
@@ -50,8 +49,10 @@ class TestNamedTuple(unittest.TestCase):
def test_main(verbose=None):
+ import collections as CollectionsModule
test_classes = [TestNamedTuple]
test_support.run_unittest(*test_classes)
+ test_support.run_doctest(CollectionsModule, verbose)
if __name__ == "__main__":
test_main(verbose=True)