Kaydet (Commit) eeeb9c44 authored tarafından Raymond Hettinger's avatar Raymond Hettinger

Accept Issac Morland's suggestion for __replace__ to allow multiple replacements

(suprisingly, this simplifies the signature, improves clarity, and is comparably fast).
Update the docs to reflect a previous change to the function name.
Add an example to the docs showing how to override the default __repr__ method.
üst b5e5d074
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
This module implements high-performance container datatypes. Currently, This module implements high-performance container datatypes. Currently,
there are two datatypes, :class:`deque` and :class:`defaultdict`, and there are two datatypes, :class:`deque` and :class:`defaultdict`, and
one datatype factory function, :func:`named_tuple`. Python already one datatype factory function, :func:`namedtuple`. Python already
includes built-in containers, :class:`dict`, :class:`list`, includes built-in containers, :class:`dict`, :class:`list`,
:class:`set`, and :class:`tuple`. In addition, the optional :mod:`bsddb` :class:`set`, and :class:`tuple`. In addition, the optional :mod:`bsddb`
module has a :meth:`bsddb.btopen` method that can be used to create in-memory module has a :meth:`bsddb.btopen` method that can be used to create in-memory
...@@ -25,7 +25,7 @@ ordered dictionaries. ...@@ -25,7 +25,7 @@ ordered dictionaries.
Added :class:`defaultdict`. Added :class:`defaultdict`.
.. versionchanged:: 2.6 .. versionchanged:: 2.6
Added :func:`named_tuple`. Added :func:`namedtuple`.
.. _deque-objects: .. _deque-objects:
...@@ -348,14 +348,14 @@ Setting the :attr:`default_factory` to :class:`set` makes the ...@@ -348,14 +348,14 @@ Setting the :attr:`default_factory` to :class:`set` makes the
.. _named-tuple-factory: .. _named-tuple-factory:
:func:`named_tuple` Factory Function for Tuples with Named Fields :func:`namedtuple` Factory Function for Tuples with Named Fields
----------------------------------------------------------------- -----------------------------------------------------------------
Named tuples assign meaning to each position in a tuple and allow for more readable, Named tuples assign meaning to each position in a tuple and allow for more readable,
self-documenting code. They can be used wherever regular tuples are used, and 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. they add the ability to access fields by name instead of position index.
.. function:: named_tuple(typename, fieldnames, [verbose]) .. function:: namedtuple(typename, fieldnames, [verbose])
Returns a new tuple subclass named *typename*. The new subclass is used to 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 create tuple-like objects that have fields accessable by attribute lookup as
...@@ -382,7 +382,7 @@ they add the ability to access fields by name instead of position index. ...@@ -382,7 +382,7 @@ they add the ability to access fields by name instead of position index.
Example:: Example::
>>> Point = named_tuple('Point', 'x y', verbose=True) >>> Point = namedtuple('Point', 'x y', verbose=True)
class Point(tuple): class Point(tuple):
'Point(x, y)' 'Point(x, y)'
__slots__ = () __slots__ = ()
...@@ -395,8 +395,8 @@ Example:: ...@@ -395,8 +395,8 @@ Example::
'Return a new dict mapping field names to their values' 'Return a new dict mapping field names to their values'
return dict(zip(('x', 'y'), self)) return dict(zip(('x', 'y'), self))
def __replace__(self, field, value): def __replace__(self, field, value):
'Return a new Point object replacing one field with a new value' 'Return a new Point object replacing specified fields with new values'
return Point(**dict(zip(('x', 'y'), self) + [(field, value)])) return Point(**dict(self.__asdict__().items() + kwds.items()))
x = property(itemgetter(0)) x = property(itemgetter(0))
y = property(itemgetter(1)) y = property(itemgetter(1))
...@@ -414,7 +414,7 @@ Example:: ...@@ -414,7 +414,7 @@ Example::
Named tuples are especially useful for assigning field names to result tuples returned Named tuples are especially useful for assigning field names to result tuples returned
by the :mod:`csv` or :mod:`sqlite3` modules:: by the :mod:`csv` or :mod:`sqlite3` modules::
EmployeeRecord = named_tuple('EmployeeRecord', 'name, age, title, department, paygrade') EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')
from itertools import starmap from itertools import starmap
import csv import csv
...@@ -453,14 +453,14 @@ two additonal methods and a read-only attribute. ...@@ -453,14 +453,14 @@ two additonal methods and a read-only attribute.
>>> p.__asdict__() >>> p.__asdict__()
{'x': 11, 'y': 22} {'x': 11, 'y': 22}
.. method:: somenamedtuple.__replace__(field, value) .. method:: somenamedtuple.__replace__(kwargs)
Return a new instance of the named tuple replacing the named *field* with a new *value*: Return a new instance of the named tuple replacing specified fields with new values:
:: ::
>>> p = Point(x=11, y=22) >>> p = Point(x=11, y=22)
>>> p.__replace__('x', 33) >>> p.__replace__(x=33)
Point(x=33, y=22) Point(x=33, y=22)
>>> for recordnum, record in inventory: >>> for recordnum, record in inventory:
...@@ -476,11 +476,22 @@ two additonal methods and a read-only attribute. ...@@ -476,11 +476,22 @@ two additonal methods and a read-only attribute.
>>> p.__fields__ # view the field names >>> p.__fields__ # view the field names
('x', 'y') ('x', 'y')
>>> Color = named_tuple('Color', 'red green blue') >>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = named_tuple('Pixel', Point.__fields__ + Color.__fields__) >>> Pixel = namedtuple('Pixel', Point.__fields__ + Color.__fields__)
>>> Pixel(11, 22, 128, 255, 0) >>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)' Pixel(x=11, y=22, red=128, green=255, blue=0)'
Since a named tuple is a regular Python class, it is easy to add or change
functionality. For example, the display format can be changed by overriding
the :meth:`__repr__` method:
::
>>> Point = namedtuple('Point', 'x y')
>>> Point.__repr__ = lambda self: 'Point(%.3f, %.3f)' % self
>>> Point(x=10, y=20)
Point(10.000, 20.000)
.. rubric:: Footnotes .. rubric:: Footnotes
.. [#] For information on the star-operator see .. [#] For information on the star-operator see
......
...@@ -24,7 +24,7 @@ def namedtuple(typename, field_names, verbose=False): ...@@ -24,7 +24,7 @@ def namedtuple(typename, field_names, verbose=False):
11 11
>>> Point(**d) # convert from a dictionary >>> Point(**d) # convert from a dictionary
Point(x=11, y=22) Point(x=11, y=22)
>>> p.__replace__('x', 100) # __replace__() is like str.replace() but targets a named field >>> p.__replace__(x=100) # __replace__() is like str.replace() but targets named fields
Point(x=100, y=22) Point(x=100, y=22)
""" """
...@@ -62,9 +62,9 @@ def namedtuple(typename, field_names, verbose=False): ...@@ -62,9 +62,9 @@ def namedtuple(typename, field_names, verbose=False):
def __asdict__(self, dict=dict, zip=zip): def __asdict__(self, dict=dict, zip=zip):
'Return a new dict mapping field names to their values' 'Return a new dict mapping field names to their values'
return dict(zip(%(field_names)r, self)) return dict(zip(%(field_names)r, self))
def __replace__(self, field, value, dict=dict, zip=zip): def __replace__(self, **kwds):
'Return a new %(typename)s object replacing one field with a new value' 'Return a new %(typename)s object replacing specified fields with new values'
return %(typename)s(**dict(zip(%(field_names)r, self) + [(field, value)])) \n''' % locals() return %(typename)s(**dict(self.__asdict__().items() + kwds.items())) \n''' % locals()
for i, name in enumerate(field_names): 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: if verbose:
...@@ -98,6 +98,10 @@ if __name__ == '__main__': ...@@ -98,6 +98,10 @@ if __name__ == '__main__':
p = Point(x=10, y=20) p = Point(x=10, y=20)
assert p == loads(dumps(p)) assert p == loads(dumps(p))
# test and demonstrate ability to override methods
Point.__repr__ = lambda self: 'Point(%.3f, %.3f)' % self
print p
import doctest import doctest
TestResults = namedtuple('TestResults', 'failed attempted') TestResults = namedtuple('TestResults', 'failed attempted')
print TestResults(*doctest.testmod()) print TestResults(*doctest.testmod())
...@@ -40,7 +40,7 @@ class TestNamedTuple(unittest.TestCase): ...@@ -40,7 +40,7 @@ class TestNamedTuple(unittest.TestCase):
self.assert_('__dict__' not in dir(p)) # verify instance has no dict self.assert_('__dict__' not in dir(p)) # verify instance has no dict
self.assert_('__weakref__' not in dir(p)) self.assert_('__weakref__' not in dir(p))
self.assertEqual(p.__fields__, ('x', 'y')) # test __fields__ attribute self.assertEqual(p.__fields__, ('x', 'y')) # test __fields__ attribute
self.assertEqual(p.__replace__('x', 1), (1, 22)) # test __replace__ method self.assertEqual(p.__replace__(x=1), (1, 22)) # test __replace__ method
self.assertEqual(p.__asdict__(), dict(x=11, y=22)) # test __dict__ method self.assertEqual(p.__asdict__(), dict(x=11, y=22)) # test __dict__ method
# Verify that __fields__ is read-only # Verify that __fields__ is read-only
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment