]> granicus.if.org Git - python/commitdiff
Minor fix-ups to named tuples:
authorRaymond Hettinger <python@rcn.com>
Fri, 4 Jan 2008 03:22:53 +0000 (03:22 +0000)
committerRaymond Hettinger <python@rcn.com>
Fri, 4 Jan 2008 03:22:53 +0000 (03:22 +0000)
* 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.

Doc/library/collections.rst
Lib/collections.py
Lib/test/test_collections.py

index 717731a776f82e86666138d20c7fcfaff0e75b08..628dbf54280f00a88deb144a372d75100122371b 100644 (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.
 
 ::
index c6d0d0f1f1e7930ce432db68eb3aeb03f4a38af4..487b1192753e7b146c73433a9044778c8e493526 100644 (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:
index edffbbe9323c4dc70e1d842d6ce49e977b4f0a03..5e71399004d5bef1a1ce04956a01fd26c0b7d6e8 100644 (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)