mirror of
https://github.com/python/cpython.git
synced 2025-11-25 21:11:09 +00:00
Improvements to NamedTuple's implementation, tests, and documentation
This commit is contained in:
parent
6290305e67
commit
5a41daf096
3 changed files with 36 additions and 25 deletions
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -24,30 +24,29 @@ def NamedTuple(typename, s):
|
|||
"""
|
||||
|
||||
field_names = s.split()
|
||||
nargs = len(field_names)
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue