Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2ebea41

Browse files
committed
Expose the namedtuple source with a _source attribute.
1 parent 843a751 commit 2ebea41

3 files changed

Lines changed: 20 additions & 53 deletions

File tree

Doc/library/collections.rst

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,9 @@ they add the ability to access fields by name instead of position index.
694694
converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword
695695
``def`` and the duplicate fieldname ``abc``.
696696

697-
If *verbose* is true, the class definition is printed just before being built.
697+
If *verbose* is true, the class definition is printed after it is
698+
built. This option is outdated; instead, it is simpler to print the
699+
:attr:`_source` attribute.
698700

699701
Named tuple instances do not have per-instance dictionaries, so they are
700702
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.
708710

709711
>>> # Basic example
710712
>>> Point = namedtuple('Point', ['x', 'y'])
711-
>>> p = Point(x=10, y=11)
712-
713-
>>> # Example using the verbose option to print the class definition
714-
>>> Point = namedtuple('Point', ['x', 'y'], verbose=True)
715-
class Point(tuple):
716-
'Point(x, y)'
717-
<BLANKLINE>
718-
__slots__ = ()
719-
<BLANKLINE>
720-
_fields = ('x', 'y')
721-
<BLANKLINE>
722-
def __new__(_cls, x, y):
723-
'Create a new instance of Point(x, y)'
724-
return _tuple.__new__(_cls, (x, y))
725-
<BLANKLINE>
726-
@classmethod
727-
def _make(cls, iterable, new=tuple.__new__, len=len):
728-
'Make a new Point object from a sequence or iterable'
729-
result = new(cls, iterable)
730-
if len(result) != 2:
731-
raise TypeError('Expected 2 arguments, got %d' % len(result))
732-
return result
733-
<BLANKLINE>
734-
def __repr__(self):
735-
'Return a nicely formatted representation string'
736-
return self.__class__.__name__ + '(x=%r, y=%r)' % self
737-
<BLANKLINE>
738-
def _asdict(self):
739-
'Return a new OrderedDict which maps field names to their values'
740-
return OrderedDict(zip(self._fields, self))
741-
<BLANKLINE>
742-
def _replace(_self, **kwds):
743-
'Return a new Point object replacing specified fields with new values'
744-
result = _self._make(map(kwds.pop, ('x', 'y'), _self))
745-
if kwds:
746-
raise ValueError('Got unexpected field names: %r' % list(kwds))
747-
return result
748-
<BLANKLINE>
749-
def __getnewargs__(self):
750-
'Return self as a plain tuple. Used by copy and pickle.'
751-
return tuple(self)
752-
<BLANKLINE>
753-
x = _property(_itemgetter(0), doc='Alias for field number 0')
754-
<BLANKLINE>
755-
y = _property(_itemgetter(1), doc='Alias for field number 1')
756-
757713
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
758714
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
759715
33
@@ -782,7 +738,7 @@ by the :mod:`csv` or :mod:`sqlite3` modules::
782738
print(emp.name, emp.title)
783739

784740
In addition to the methods inherited from tuples, named tuples support
785-
three additional methods and one attribute. To prevent conflicts with
741+
three additional methods and two attributes. To prevent conflicts with
786742
field names, the method and attribute names start with an underscore.
787743

788744
.. classmethod:: somenamedtuple._make(iterable)
@@ -820,6 +776,15 @@ field names, the method and attribute names start with an underscore.
820776
>>> for partnum, record in inventory.items():
821777
... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
822778

779+
.. attribute:: somenamedtuple._source
780+
781+
A string with the pure Python source code used to create the named
782+
tuple class. The source makes the named tuple self-documenting.
783+
It can be printed, executed using :func:`exec`, or saved to a file
784+
and imported.
785+
786+
.. versionadded:: 3.3
787+
823788
.. attribute:: somenamedtuple._fields
824789

825790
Tuple of strings listing the field names. Useful for introspection

Lib/collections/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,13 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
332332
raise ValueError('Type names and field names cannot be a keyword: %r' % name)
333333
if name[0].isdigit():
334334
raise ValueError('Type names and field names cannot start with a number: %r' % name)
335-
seen_names = set()
335+
seen = set()
336336
for name in field_names:
337337
if name.startswith('_') and not rename:
338338
raise ValueError('Field names cannot start with an underscore: %r' % name)
339-
if name in seen_names:
339+
if name in seen:
340340
raise ValueError('Encountered duplicate field name: %r' % name)
341-
seen_names.add(name)
341+
seen.add(name)
342342

343343
# Fill-in the class template
344344
class_definition = _class_template.format(
@@ -350,8 +350,6 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
350350
field_defs = '\n'.join(_field_template.format(index=index, name=name)
351351
for index, name in enumerate(field_names))
352352
)
353-
if verbose:
354-
print(class_definition)
355353

356354
# Execute the class definition string in a temporary namespace and
357355
# 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):
361359
except SyntaxError as e:
362360
raise SyntaxError(e.msg + ':\n\n' + class_definition)
363361
result = namespace[typename]
362+
result._source = class_definition
363+
if verbose:
364+
print(result._source)
364365

365366
# For pickling to work, the __module__ variable needs to be set to the frame
366367
# where the named tuple is created. Bypass this step in enviroments where

Lib/test/test_collections.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def test_factory(self):
127127
self.assertEqual(Point.__module__, __name__)
128128
self.assertEqual(Point.__getitem__, tuple.__getitem__)
129129
self.assertEqual(Point._fields, ('x', 'y'))
130+
self.assertIn('class Point(tuple)', Point._source)
130131

131132
self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi') # type has non-alpha char
132133
self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword

0 commit comments

Comments
 (0)