summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2011-03-23 19:52:23 (GMT)
committerRaymond Hettinger <python@rcn.com>2011-03-23 19:52:23 (GMT)
commit2ebea41d315bb42a6d6983137bf5fdb01d3f1a95 (patch)
treecdfff191e34b120df6271ab3a77a692548d832d8
parent843a751369856f97f936c94a562b461254941b35 (diff)
downloadcpython-2ebea41d315bb42a6d6983137bf5fdb01d3f1a95.zip
cpython-2ebea41d315bb42a6d6983137bf5fdb01d3f1a95.tar.gz
cpython-2ebea41d315bb42a6d6983137bf5fdb01d3f1a95.tar.bz2
Expose the namedtuple source with a _source attribute.
-rw-r--r--Doc/library/collections.rst61
-rw-r--r--Lib/collections/__init__.py11
-rw-r--r--Lib/test/test_collections.py1
3 files changed, 20 insertions, 53 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 411d5f6..e531d40 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -694,7 +694,9 @@ they add the ability to access fields by name instead of position index.
converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword
``def`` and the duplicate fieldname ``abc``.
- If *verbose* is true, the class definition is printed just before being built.
+ If *verbose* is true, the class definition is printed after it is
+ built. This option is outdated; instead, it is simpler to print the
+ :attr:`_source` attribute.
Named tuple instances do not have per-instance dictionaries, so they are
lightweight and require no more memory than regular tuples.
@@ -708,52 +710,6 @@ they add the ability to access fields by name instead of position index.
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
- >>> p = Point(x=10, y=11)
-
- >>> # Example using the verbose option to print the class definition
- >>> Point = namedtuple('Point', ['x', 'y'], verbose=True)
- class Point(tuple):
- 'Point(x, y)'
- <BLANKLINE>
- __slots__ = ()
- <BLANKLINE>
- _fields = ('x', 'y')
- <BLANKLINE>
- def __new__(_cls, x, y):
- 'Create a new instance of Point(x, y)'
- return _tuple.__new__(_cls, (x, y))
- <BLANKLINE>
- @classmethod
- def _make(cls, iterable, new=tuple.__new__, len=len):
- 'Make a new Point object from a sequence or iterable'
- result = new(cls, iterable)
- if len(result) != 2:
- raise TypeError('Expected 2 arguments, got %d' % len(result))
- return result
- <BLANKLINE>
- def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + '(x=%r, y=%r)' % self
- <BLANKLINE>
- def _asdict(self):
- 'Return a new OrderedDict which maps field names to their values'
- return OrderedDict(zip(self._fields, self))
- <BLANKLINE>
- def _replace(_self, **kwds):
- 'Return a new Point object replacing specified fields with new values'
- result = _self._make(map(kwds.pop, ('x', 'y'), _self))
- if kwds:
- raise ValueError('Got unexpected field names: %r' % list(kwds))
- return result
- <BLANKLINE>
- def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return tuple(self)
- <BLANKLINE>
- x = _property(_itemgetter(0), doc='Alias for field number 0')
- <BLANKLINE>
- y = _property(_itemgetter(1), doc='Alias for field number 1')
-
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
@@ -782,7 +738,7 @@ 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 additional methods and one attribute. To prevent conflicts with
+three additional methods and two attributes. To prevent conflicts with
field names, the method and attribute names start with an underscore.
.. classmethod:: somenamedtuple._make(iterable)
@@ -820,6 +776,15 @@ field names, the method and attribute names start with an underscore.
>>> for partnum, record in inventory.items():
... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
+.. attribute:: somenamedtuple._source
+
+ A string with the pure Python source code used to create the named
+ tuple class. The source makes the named tuple self-documenting.
+ It can be printed, executed using :func:`exec`, or saved to a file
+ and imported.
+
+ .. versionadded:: 3.3
+
.. attribute:: somenamedtuple._fields
Tuple of strings listing the field names. Useful for introspection
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
index b75b4d7..652e4f1 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -332,13 +332,13 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
raise ValueError('Type names and field names cannot be a keyword: %r' % name)
if name[0].isdigit():
raise ValueError('Type names and field names cannot start with a number: %r' % name)
- seen_names = set()
+ seen = set()
for name in field_names:
if name.startswith('_') and not rename:
raise ValueError('Field names cannot start with an underscore: %r' % name)
- if name in seen_names:
+ if name in seen:
raise ValueError('Encountered duplicate field name: %r' % name)
- seen_names.add(name)
+ seen.add(name)
# Fill-in the class template
class_definition = _class_template.format(
@@ -350,8 +350,6 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
field_defs = '\n'.join(_field_template.format(index=index, name=name)
for index, name in enumerate(field_names))
)
- if verbose:
- print(class_definition)
# Execute the class definition string in a temporary namespace and
# support tracing utilities by setting a value for frame.f_globals['__name__']
@@ -361,6 +359,9 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
except SyntaxError as e:
raise SyntaxError(e.msg + ':\n\n' + class_definition)
result = namespace[typename]
+ result._source = class_definition
+ if verbose:
+ print(result._source)
# For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in enviroments where
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index d71fb01..cdc7db9 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -127,6 +127,7 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(Point.__module__, __name__)
self.assertEqual(Point.__getitem__, tuple.__getitem__)
self.assertEqual(Point._fields, ('x', 'y'))
+ self.assertIn('class Point(tuple)', Point._source)
self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi') # type has non-alpha char
self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword