]> granicus.if.org Git - python/commitdiff
Handle corner cased on 0-tuples and 1-tuples. Add verbose option so people can see...
authorRaymond Hettinger <python@rcn.com>
Tue, 18 Sep 2007 03:33:19 +0000 (03:33 +0000)
committerRaymond Hettinger <python@rcn.com>
Tue, 18 Sep 2007 03:33:19 +0000 (03:33 +0000)
Doc/library/collections.rst
Lib/collections.py
Lib/test/test_collections.py

index 7b639b380dee2bf0f6008875c345a44f22d73807..29860712f751abab1de1126f1b5142752ec3f4db 100644 (file)
@@ -364,7 +364,7 @@ Setting the :attr:`default_factory` to :class:`set` makes the
 --------------------------------------------
 
 
-.. function:: NamedTuple(typename, fieldnames)
+.. function:: NamedTuple(typename, fieldnames, [verbose])
 
    Returns a new tuple subclass named *typename*.  The new subclass is used to
    create tuple-like objects that have fields accessable by attribute lookup as
@@ -412,6 +412,23 @@ Setting the :attr:`default_factory` to :class:`set` makes the
       >>> print Color(*m.popitem())
       Color(name='blue', code=3)
 
+   If *verbose* is true, the *NamedTuple* call will print the class definition::
+
+       >>> Point = NamedTuple('Point', 'x y', verbose=True)
+       class Point(tuple):
+               'Point(x, y)'
+               __slots__ = ()
+               __fields__ = ('x', 'y')
+               def __new__(cls, x, y):
+                   return tuple.__new__(cls, (x, y))
+               def __repr__(self):
+                   return 'Point(x=%r, y=%r)' % self
+               def __replace__(self, field, value):
+                   'Return a new Point object replacing one field with a new value'
+                   return Point(**dict(zip(('x', 'y'), self) + [(field, value)]))
+               x = property(itemgetter(0))
+               y = property(itemgetter(1))
+
 In addition to the methods inherited from tuples, named tuples support
 an additonal method and an informational read-only attribute.
 
index c2b1176fe51a7bb00ea03caa8f5927b655a530d4..816f864c98fa1acdb642791f8352b41d6980a573 100644 (file)
@@ -4,7 +4,7 @@ from _collections import deque, defaultdict
 from operator import itemgetter as _itemgetter
 import sys as _sys
 
-def NamedTuple(typename, s):
+def NamedTuple(typename, s, verbose=False):
     """Returns a new subclass of tuple with named fields.
 
     >>> Point = NamedTuple('Point', 'x y')
@@ -28,25 +28,26 @@ def NamedTuple(typename, s):
 
     """
 
-    field_names = tuple(s.replace(',', ' ').split())   # names separated by spaces and/or commas
+    field_names = tuple(s.replace(',', ' ').split())    # names separated by spaces and/or commas
     if not ''.join((typename,) + field_names).replace('_', '').isalnum():
         raise ValueError('Type names and field names can only contain alphanumeric characters and underscores')
-    argtxt = ', '.join(field_names)
+    argtxt = repr(field_names).replace("'", "")[1:-1]   # tuple repr without parens or quotes
     reprtxt = ', '.join('%s=%%r' % name for name in field_names)
     template = '''class %(typename)s(tuple):
         '%(typename)s(%(argtxt)s)'
         __slots__ = ()
         __fields__ = %(field_names)r
         def __new__(cls, %(argtxt)s):
-            return tuple.__new__(cls, (%(argtxt)s,))
+            return tuple.__new__(cls, (%(argtxt)s))
         def __repr__(self):
             return '%(typename)s(%(reprtxt)s)' %% self
         def __replace__(self, field, value):
             'Return a new %(typename)s object replacing one field with a new value'
-            return %(typename)s(**dict(zip(%(field_names)r, self) + [(field, value)]))
-    ''' % locals()
+            return %(typename)s(**dict(zip(%(field_names)r, self) + [(field, value)]))  \n''' % locals()
     for i, name in enumerate(field_names):
-        template += '\n        %s = property(itemgetter(%d))\n' % (name, i)
+        template += '        %s = property(itemgetter(%d))\n' % (name, i)
+    if verbose:
+        print template
     m = dict(itemgetter=_itemgetter)
     exec template in m
     result = m[typename]
@@ -62,7 +63,7 @@ def NamedTuple(typename, s):
 if __name__ == '__main__':
     # verify that instances can be pickled
     from cPickle import loads, dumps
-    Point = NamedTuple('Point', 'x, y')
+    Point = NamedTuple('Point', 'x, y', True)
     p = Point(x=10, y=20)
     assert p == loads(dumps(p))
 
index 94015b42d3c14dc5a996783e8845a6e5c421fbe4..ab36ad87b71d70e4556748858c94591bc6861b99 100644 (file)
@@ -58,6 +58,12 @@ class TestNamedTuple(unittest.TestCase):
         self.assertRaises(AttributeError, eval, 'p.z', locals())
 
 
+    def test_odd_sizes(self):
+        Zero = NamedTuple('Zero', '')
+        self.assertEqual(Zero(), ())
+        Dot = NamedTuple('Dot', 'd')
+        self.assertEqual(Dot(1), (1,))
+
 def test_main(verbose=None):
     import collections as CollectionsModule
     test_classes = [TestNamedTuple]