summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2008-01-04 03:22:53 (GMT)
committerRaymond Hettinger <python@rcn.com>2008-01-04 03:22:53 (GMT)
commite0734e7dc0dcccc91ed657191b804b3a846ad3f6 (patch)
tree70cb770b9fb3fbca03fcbb1a53df10f69612a78b
parent123d5c9396a6c12d418bbdf6d7ec861537f4c199 (diff)
downloadcpython-e0734e7dc0dcccc91ed657191b804b3a846ad3f6.zip
cpython-e0734e7dc0dcccc91ed657191b804b3a846ad3f6.tar.gz
cpython-e0734e7dc0dcccc91ed657191b804b3a846ad3f6.tar.bz2
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.
-rw-r--r--Doc/library/collections.rst23
-rw-r--r--Lib/collections.py6
-rw-r--r--Lib/test/test_collections.py9
3 files changed, 16 insertions, 22 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 717731a..628dbf5 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -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.
::
diff --git a/Lib/collections.py b/Lib/collections.py
index c6d0d0f..487b119 100644
--- a/Lib/collections.py
+++ b/Lib/collections.py
@@ -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:
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index edffbbe..5e71399 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -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)