Fix field name conflicts for named tuples.

This commit is contained in:
Raymond Hettinger 2009-05-27 00:38:24 +00:00
parent e261ae0d46
commit 089ba7f690
4 changed files with 25 additions and 13 deletions

View File

@ -647,8 +647,8 @@ Example:
<BLANKLINE>
_fields = ('x', 'y')
<BLANKLINE>
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
def __new__(_cls, x, y):
return _tuple.__new__(_cls, (x, y))
<BLANKLINE>
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
@ -665,9 +665,9 @@ Example:
'Return a new OrderedDict which maps field names to their values'
return OrderedDict(zip(self._fields, self))
<BLANKLINE>
def _replace(self, **kwds):
def _replace(_self, **kwds):
'Return a new Point object replacing specified fields with new values'
result = self._make(map(kwds.pop, ('x', 'y'), self))
result = _self._make(map(kwds.pop, ('x', 'y'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return result
@ -675,8 +675,8 @@ Example:
def __getnewargs__(self):
return tuple(self)
<BLANKLINE>
x = property(itemgetter(0))
y = property(itemgetter(1))
x = _property(_itemgetter(0))
y = _property(_itemgetter(1))
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)

View File

@ -232,8 +232,8 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
'%(typename)s(%(argtxt)s)' \n
__slots__ = () \n
_fields = %(field_names)r \n
def __new__(cls, %(argtxt)s):
return tuple.__new__(cls, (%(argtxt)s)) \n
def __new__(_cls, %(argtxt)s):
return _tuple.__new__(_cls, (%(argtxt)s)) \n
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new %(typename)s object from a sequence or iterable'
@ -246,23 +246,23 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
def _asdict(self):
'Return a new OrderedDict which maps field names to their values'
return OrderedDict(zip(self._fields, self)) \n
def _replace(self, **kwds):
def _replace(_self, **kwds):
'Return a new %(typename)s object replacing specified fields with new values'
result = self._make(map(kwds.pop, %(field_names)r, self))
result = _self._make(map(kwds.pop, %(field_names)r, _self))
if kwds:
raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
return result \n
def __getnewargs__(self):
return tuple(self) \n\n''' % locals()
for i, name in enumerate(field_names):
template += ' %s = property(itemgetter(%d))\n' % (name, i)
template += ' %s = _property(_itemgetter(%d))\n' % (name, i)
if verbose:
print(template)
# Execute the template string in a temporary namespace and
# support tracing utilities by setting a value for frame.f_globals['__name__']
namespace = dict(itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
OrderedDict=OrderedDict)
namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
OrderedDict=OrderedDict, _property=property, _tuple=tuple)
try:
exec(template, namespace)
except SyntaxError as e:

View File

@ -173,6 +173,15 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(p, q)
self.assertEqual(p._fields, q._fields)
def test_name_conflicts(self):
# Some names like "self", "cls", "tuple", "itemgetter", and "property"
# failed when used as field names. Test to make sure these now work.
T = namedtuple('T', 'itemgetter property self cls tuple')
t = T(1, 2, 3, 4, 5)
self.assertEqual(t, (1,2,3,4,5))
newt = t._replace(itemgetter=10, property=20, self=30, cls=40, tuple=50)
self.assertEqual(newt, (10,20,30,40,50))
class ABCTestCase(unittest.TestCase):
def validate_abstract_methods(self, abc, *names):

View File

@ -37,6 +37,9 @@ Library
- Issue #6118: urllib.parse.quote_plus ignored the encoding and errors
arguments for strings with a space in them.
- collections.namedtuple() was not working with the following field
names: cls, self, tuple, itemgetter, and property.
- In unittest, using a skipping decorator on a class is now equivalent to
skipping every test on the class. The ClassTestSuite class has been removed.