mirror of https://github.com/python/cpython
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:
parent
123d5c9396
commit
e0734e7dc0
|
@ -388,6 +388,8 @@ Example::
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
_fields = ('x', 'y')
|
||||||
|
|
||||||
def __new__(cls, x, y):
|
def __new__(cls, x, y):
|
||||||
return tuple.__new__(cls, (x, y))
|
return tuple.__new__(cls, (x, y))
|
||||||
|
|
||||||
|
@ -402,11 +404,7 @@ Example::
|
||||||
|
|
||||||
def _replace(self, **kwds):
|
def _replace(self, **kwds):
|
||||||
'Return a new Point object replacing specified fields with new values'
|
'Return a new Point object replacing specified fields with new values'
|
||||||
return Point._cast(map(kwds.get, ('x', 'y'), self))
|
return self.__class__._cast(map(kwds.get, ('x', 'y'), self))
|
||||||
|
|
||||||
@property
|
|
||||||
def _fields(self):
|
|
||||||
return ('x', 'y')
|
|
||||||
|
|
||||||
x = property(itemgetter(0))
|
x = property(itemgetter(0))
|
||||||
y = property(itemgetter(1))
|
y = property(itemgetter(1))
|
||||||
|
@ -439,17 +437,22 @@ by the :mod:`csv` or :mod:`sqlite3` modules::
|
||||||
print emp.name, emp.title
|
print emp.name, emp.title
|
||||||
|
|
||||||
In addition to the methods inherited from tuples, named tuples support
|
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)
|
.. method:: namedtuple._cast(iterable)
|
||||||
|
|
||||||
Class method returning a new instance taking the positional arguments from the *iterable*.
|
Class method returning a new instance taking the positional arguments from the
|
||||||
Useful for casting existing sequences and iterables to named tuples:
|
*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]
|
>>> 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)
|
Point(x=11, y=22)
|
||||||
|
|
||||||
.. method:: somenamedtuple._asdict()
|
.. method:: somenamedtuple._asdict()
|
||||||
|
@ -476,7 +479,7 @@ three additonal methods and a read-only attribute.
|
||||||
|
|
||||||
.. attribute:: somenamedtuple._fields
|
.. 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.
|
and for creating new named tuple types from existing named tuples.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
|
@ -60,6 +60,7 @@ def namedtuple(typename, field_names, verbose=False):
|
||||||
template = '''class %(typename)s(tuple):
|
template = '''class %(typename)s(tuple):
|
||||||
'%(typename)s(%(argtxt)s)' \n
|
'%(typename)s(%(argtxt)s)' \n
|
||||||
__slots__ = () \n
|
__slots__ = () \n
|
||||||
|
_fields = %(field_names)r \n
|
||||||
def __new__(cls, %(argtxt)s):
|
def __new__(cls, %(argtxt)s):
|
||||||
return tuple.__new__(cls, (%(argtxt)s)) \n
|
return tuple.__new__(cls, (%(argtxt)s)) \n
|
||||||
_cast = classmethod(tuple.__new__) \n
|
_cast = classmethod(tuple.__new__) \n
|
||||||
|
@ -70,10 +71,7 @@ def namedtuple(typename, field_names, verbose=False):
|
||||||
return {%(dicttxt)s} \n
|
return {%(dicttxt)s} \n
|
||||||
def _replace(self, **kwds):
|
def _replace(self, **kwds):
|
||||||
'Return a new %(typename)s object replacing specified fields with new values'
|
'Return a new %(typename)s object replacing specified fields with new values'
|
||||||
return %(typename)s._cast(map(kwds.get, %(field_names)r, self)) \n
|
return self.__class__._cast(map(kwds.get, %(field_names)r, self)) \n\n''' % locals()
|
||||||
@property
|
|
||||||
def _fields(self):
|
|
||||||
return %(field_names)r \n\n''' % locals()
|
|
||||||
for i, name in enumerate(field_names):
|
for i, name in enumerate(field_names):
|
||||||
template += ' %s = property(itemgetter(%d))\n' % (name, i)
|
template += ' %s = property(itemgetter(%d))\n' % (name, i)
|
||||||
if verbose:
|
if verbose:
|
||||||
|
|
|
@ -17,6 +17,7 @@ class TestNamedTuple(unittest.TestCase):
|
||||||
self.assertEqual(Point.__slots__, ())
|
self.assertEqual(Point.__slots__, ())
|
||||||
self.assertEqual(Point.__module__, __name__)
|
self.assertEqual(Point.__module__, __name__)
|
||||||
self.assertEqual(Point.__getitem__, tuple.__getitem__)
|
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, 'abc%', 'efg ghi') # type has non-alpha char
|
||||||
self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword
|
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._replace(x=1), (1, 22)) # test _replace method
|
||||||
self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict 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
|
# verify that field string can have commas
|
||||||
Point = namedtuple('Point', 'x, y')
|
Point = namedtuple('Point', 'x, y')
|
||||||
p = Point(x=11, y=22)
|
p = Point(x=11, y=22)
|
||||||
|
|
Loading…
Reference in New Issue