Minor fix-ups to named tuples:

* Make the _replace() method respect subclassing.

* Using property() to make _fields read-only wasn't a good idea.
  It caused len(Point._fields) to fail.

* Add note to _cast() about length checking and alternative with the star-operator.
This commit is contained in:
Raymond Hettinger 2008-01-04 03:22:53 +00:00
parent 123d5c9396
commit e0734e7dc0
3 changed files with 16 additions and 22 deletions

View File

@ -388,6 +388,8 @@ Example::
__slots__ = ()
_fields = ('x', 'y')
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
@ -402,11 +404,7 @@ Example::
def _replace(self, **kwds):
'Return a new Point object replacing specified fields with new values'
return Point._cast(map(kwds.get, ('x', 'y'), self))
@property
def _fields(self):
return ('x', 'y')
return self.__class__._cast(map(kwds.get, ('x', 'y'), self))
x = property(itemgetter(0))
y = property(itemgetter(1))
@ -439,17 +437,22 @@ by the :mod:`csv` or :mod:`sqlite3` modules::
print emp.name, emp.title
In addition to the methods inherited from tuples, named tuples support
three additonal methods and a read-only attribute.
three additonal methods and one attribute.
.. method:: namedtuple._cast(iterable)
Class method returning a new instance taking the positional arguments from the *iterable*.
Useful for casting existing sequences and iterables to named tuples:
Class method returning a new instance taking the positional arguments from the
*iterable*. Useful for casting existing sequences and iterables to named tuples.
This fast constructor does not check the length of the inputs. To achieve the
same effect with length checking, use the star-operator instead.
::
>>> t = [11, 22]
>>> Point._cast(t)
>>> Point._cast(t) # fast conversion
Point(x=11, y=22)
>>> Point(*t) # slow conversion with length checking
Point(x=11, y=22)
.. method:: somenamedtuple._asdict()
@ -476,7 +479,7 @@ three additonal methods and a read-only attribute.
.. attribute:: somenamedtuple._fields
Return a tuple of strings listing the field names. This is useful for introspection
Tuple of strings listing the field names. This is useful for introspection
and for creating new named tuple types from existing named tuples.
::

View File

@ -60,6 +60,7 @@ def namedtuple(typename, field_names, verbose=False):
template = '''class %(typename)s(tuple):
'%(typename)s(%(argtxt)s)' \n
__slots__ = () \n
_fields = %(field_names)r \n
def __new__(cls, %(argtxt)s):
return tuple.__new__(cls, (%(argtxt)s)) \n
_cast = classmethod(tuple.__new__) \n
@ -70,10 +71,7 @@ def namedtuple(typename, field_names, verbose=False):
return {%(dicttxt)s} \n
def _replace(self, **kwds):
'Return a new %(typename)s object replacing specified fields with new values'
return %(typename)s._cast(map(kwds.get, %(field_names)r, self)) \n
@property
def _fields(self):
return %(field_names)r \n\n''' % locals()
return self.__class__._cast(map(kwds.get, %(field_names)r, self)) \n\n''' % locals()
for i, name in enumerate(field_names):
template += ' %s = property(itemgetter(%d))\n' % (name, i)
if verbose:

View File

@ -17,6 +17,7 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(Point.__slots__, ())
self.assertEqual(Point.__module__, __name__)
self.assertEqual(Point.__getitem__, tuple.__getitem__)
self.assertEqual(Point._fields, ('x', 'y'))
self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi') # type has non-alpha char
self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword
@ -51,14 +52,6 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method
self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method
# Verify that _fields is read-only
try:
p._fields = ('F1' ,'F2')
except AttributeError:
pass
else:
self.fail('The _fields attribute needs to be read-only')
# verify that field string can have commas
Point = namedtuple('Point', 'x, y')
p = Point(x=11, y=22)