Merged revisions 69466,69480 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r69466 | raymond.hettinger | 2009-02-09 12:39:41 -0600 (Mon, 09 Feb 2009) | 3 lines

  Issue 5171: itertools.product docstring missing 'repeat' argument
........
  r69480 | raymond.hettinger | 2009-02-09 19:24:05 -0600 (Mon, 09 Feb 2009) | 1 line

  Issue 1818: collections.namedtuple() to support automatic renaming of invalid fieldnames.
........
This commit is contained in:
Benjamin Peterson 2009-02-10 02:41:10 +00:00
parent 3a409e91d8
commit a86f2c06fd
5 changed files with 39 additions and 3 deletions

View File

@ -597,7 +597,7 @@ Named tuples assign meaning to each position in a tuple and allow for more reada
self-documenting code. They can be used wherever regular tuples are used, and
they add the ability to access fields by name instead of position index.
.. function:: namedtuple(typename, field_names, [verbose])
.. function:: namedtuple(typename, field_names, [verbose], [rename])
Returns a new tuple subclass named *typename*. The new subclass is used to
create tuple-like objects that have fields accessible by attribute lookup as
@ -615,11 +615,19 @@ they add the ability to access fields by name instead of position index.
a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*,
or *raise*.
If *rename* is true, invalid fieldnames are automatically replaced
with positional names. For example, ``['abc', 'def', 'ghi', 'abc']`` is
converted to ``['abc', '_2', 'ghi', '_4']``, eliminating the keyword
``def`` and the duplicate fieldname ``abc``.
If *verbose* is true, the class definition is printed just before being built.
Named tuple instances do not have per-instance dictionaries, so they are
lightweight and require no more memory than regular tuples.
.. versionchanged:: 2.7
added support for *rename*.
Example:
.. doctest::

View File

@ -18,7 +18,7 @@ from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
### namedtuple
################################################################################
def namedtuple(typename, field_names, verbose=False):
def namedtuple(typename, field_names, verbose=False, rename=False):
"""Returns a new subclass of tuple with named fields.
>>> Point = namedtuple('Point', 'x y')
@ -47,6 +47,16 @@ def namedtuple(typename, field_names, verbose=False):
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
field_names = tuple(map(str, field_names))
if rename:
names = list(field_names)
seen = set()
for i, name in enumerate(names):
if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name)
or not name or name[0].isdigit() or name.startswith('_')
or name in seen):
names[i] = '_%d' % (i+1)
seen.add(name)
field_names = tuple(names)
for name in (typename,) + field_names:
if not all(c.isalnum() or c=='_' for c in name):
raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
@ -56,7 +66,7 @@ def namedtuple(typename, field_names, verbose=False):
raise ValueError('Type names and field names cannot start with a number: %r' % name)
seen_names = set()
for name in field_names:
if name.startswith('_'):
if name.startswith('_') and not rename:
raise ValueError('Field names cannot start with an underscore: %r' % name)
if name in seen_names:
raise ValueError('Encountered duplicate field name: %r' % name)

View File

@ -47,6 +47,17 @@ class TestNamedTuple(unittest.TestCase):
self.assertRaises(TypeError, Point._make, [11]) # catch too few args
self.assertRaises(TypeError, Point._make, [11, 22, 33]) # catch too many args
def test_name_fixer(self):
for spec, renamed in [
[('efg', 'g%hi'), ('efg', '_2')], # field with non-alpha char
[('abc', 'class'), ('abc', '_2')], # field has keyword
[('8efg', '9ghi'), ('_1', '_2')], # field starts with digit
[('abc', '_efg'), ('abc', '_2')], # field with leading underscore
[('abc', 'efg', 'efg', 'ghi'), ('abc', 'efg', '_3', 'ghi')], # duplicate field
[('abc', '', 'x'), ('abc', '_2', 'x')], # fieldname is a space
]:
self.assertEqual(namedtuple('NT', spec, rename=True)._fields, renamed)
def test_instance(self):
Point = namedtuple('Point', 'x y')
p = Point(11, 22)

View File

@ -170,6 +170,10 @@ Library
- Issue #5122: Synchronize tk load failure check to prevent a potential
deadlock.
- Issue #1818: collections.namedtuple() now supports a keyword argument
'rename' which lets invalid fieldnames be automatically converted to
positional names in the form, _1, _2, ...
- Issue #4890: Handle empty text search pattern in Tkinter.Text.search.
- Issue #4512 (part 2): Promote ``ZipImporter._get_filename()`` to be a

View File

@ -1791,6 +1791,9 @@ For example, product(A, B) returns the same as: ((x,y) for x in A for y in B).\
The leftmost iterators are in the outermost for-loop, so the output tuples\n\
cycle in a manner similar to an odometer (with the rightmost element changing\n\
on every iteration).\n\n\
To compute the product of an iterable with itself, specify the number\n\
of repetitions with the optional repeat keyword argument. For example,\n\
product(A, repeat=4) means the same as product(A, A, A, A).\n\n\
product('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\n\
product((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ...");