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

Cleaner method naming convention

üst 90e10e79
...@@ -368,8 +368,8 @@ they add the ability to access fields by name instead of position index. ...@@ -368,8 +368,8 @@ they add the ability to access fields by name instead of position index.
can be specified as a list of strings (such as ['x', 'y']). can be specified as a list of strings (such as ['x', 'y']).
Any valid Python identifier may be used for a fieldname except for names Any valid Python identifier may be used for a fieldname except for names
starting and ending with double underscores. Valid identifiers consist of starting with an underscore. Valid identifiers consist of letters, digits,
letters, digits, and underscores but do not start with a digit and cannot be and underscores but do not start with a digit or underscore and cannot be
a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, *print*, a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, *print*,
or *raise*. or *raise*.
...@@ -386,15 +386,15 @@ Example:: ...@@ -386,15 +386,15 @@ Example::
class Point(tuple): class Point(tuple):
'Point(x, y)' 'Point(x, y)'
__slots__ = () __slots__ = ()
__fields__ = ('x', 'y') _fields = ('x', 'y')
def __new__(cls, x, y): def __new__(cls, x, y):
return tuple.__new__(cls, (x, y)) return tuple.__new__(cls, (x, y))
def __repr__(self): def __repr__(self):
return 'Point(x=%r, y=%r)' % self return 'Point(x=%r, y=%r)' % self
def __asdict__(self): def _asdict(self):
'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, **kwds): def _replace(self, **kwds):
'Return a new Point object replacing specified fields with new values' 'Return a new Point object replacing specified fields with new values'
return Point(**dict(zip(('x', 'y'), self), **kwds)) return Point(**dict(zip(('x', 'y'), self), **kwds))
x = property(itemgetter(0)) x = property(itemgetter(0))
...@@ -444,40 +444,40 @@ When casting a dictionary to a named tuple, use the double-star-operator:: ...@@ -444,40 +444,40 @@ When casting a dictionary to a named tuple, use the double-star-operator::
In addition to the methods inherited from tuples, named tuples support In addition to the methods inherited from tuples, named tuples support
two additonal methods and a read-only attribute. two additonal methods and a read-only attribute.
.. method:: somenamedtuple.__asdict__() .. method:: somenamedtuple._asdict()
Return a new dict which maps field names to their corresponding values: Return a new dict which maps field names to their corresponding values:
:: ::
>>> p.__asdict__() >>> p._asdict()
{'x': 11, 'y': 22} {'x': 11, 'y': 22}
.. method:: somenamedtuple.__replace__(kwargs) .. method:: somenamedtuple._replace(kwargs)
Return a new instance of the named tuple replacing specified fields with new values: 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 partnum, record in inventory.items(): >>> for partnum, record in inventory.items():
... inventory[partnum] = record.__replace__(price=newprices[partnum], updated=time.now()) ... inventory[partnum] = record._replace(price=newprices[partnum], updated=time.now())
.. attribute:: somenamedtuple.__fields__ .. attribute:: somenamedtuple._fields
Return a tuple of strings listing the field names. This is useful for introspection Return a tuple of strings listing the field names. This is useful for introspection
and for creating new named tuple types from existing named tuples. and for creating new named tuple types from existing named tuples.
:: ::
>>> p.__fields__ # view the field names >>> p._fields # view the field names
('x', 'y') ('x', 'y')
>>> Color = namedtuple('Color', 'red green blue') >>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('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)'
...@@ -493,13 +493,13 @@ the :meth:`__repr__` method: ...@@ -493,13 +493,13 @@ the :meth:`__repr__` method:
Point(10.000, 20.000) Point(10.000, 20.000)
Default values can be implemented by starting with a prototype instance Default values can be implemented by starting with a prototype instance
and customizing it with :meth:`__replace__`: and customizing it with :meth:`_replace`:
:: ::
>>> Account = namedtuple('Account', 'owner balance transaction_count') >>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> model_account = Account('<owner name>', 0.0, 0) >>> model_account = Account('<owner name>', 0.0, 0)
>>> johns_account = model_account.__replace__(owner='John') >>> johns_account = model_account._replace(owner='John')
.. rubric:: Footnotes .. rubric:: Footnotes
......
...@@ -26,12 +26,12 @@ def namedtuple(typename, field_names, verbose=False): ...@@ -26,12 +26,12 @@ def namedtuple(typename, field_names, verbose=False):
(11, 22) (11, 22)
>>> p.x + p.y # fields also accessable by name >>> p.x + p.y # fields also accessable by name
33 33
>>> d = p.__asdict__() # convert to a dictionary >>> d = p._asdict() # convert to a dictionary
>>> d['x'] >>> d['x']
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 named fields >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
Point(x=100, y=22) Point(x=100, y=22)
""" """
...@@ -49,8 +49,8 @@ def namedtuple(typename, field_names, verbose=False): ...@@ -49,8 +49,8 @@ def namedtuple(typename, field_names, verbose=False):
raise ValueError('Type names and field names cannot start with a number: %r' % name) raise ValueError('Type names and field names cannot start with a number: %r' % name)
seen_names = set() seen_names = set()
for name in field_names: for name in field_names:
if name.startswith('__') and name.endswith('__') and len(name) > 3: if name.startswith('_'):
raise ValueError('Field names cannot start and end with double underscores: %r' % name) raise ValueError('Field names cannot start with an underscore: %r' % name)
if name in seen_names: if name in seen_names:
raise ValueError('Encountered duplicate field name: %r' % name) raise ValueError('Encountered duplicate field name: %r' % name)
seen_names.add(name) seen_names.add(name)
...@@ -61,15 +61,15 @@ def namedtuple(typename, field_names, verbose=False): ...@@ -61,15 +61,15 @@ def namedtuple(typename, field_names, verbose=False):
template = '''class %(typename)s(tuple): template = '''class %(typename)s(tuple):
'%(typename)s(%(argtxt)s)' '%(typename)s(%(argtxt)s)'
__slots__ = () __slots__ = ()
__fields__ = property(lambda self: %(field_names)r) _fields = property(lambda self: %(field_names)r)
def __new__(cls, %(argtxt)s): def __new__(cls, %(argtxt)s):
return tuple.__new__(cls, (%(argtxt)s)) return tuple.__new__(cls, (%(argtxt)s))
def __repr__(self): def __repr__(self):
return '%(typename)s(%(reprtxt)s)' %% self return '%(typename)s(%(reprtxt)s)' %% self
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, **kwds): def _replace(self, **kwds):
'Return a new %(typename)s object replacing specified fields with new values' 'Return a new %(typename)s object replacing specified fields with new values'
return %(typename)s(**dict(zip(%(field_names)r, self), **kwds)) \n''' % locals() return %(typename)s(**dict(zip(%(field_names)r, self), **kwds)) \n''' % locals()
for i, name in enumerate(field_names): for i, name in enumerate(field_names):
......
...@@ -25,11 +25,11 @@ class TestNamedTuple(unittest.TestCase): ...@@ -25,11 +25,11 @@ class TestNamedTuple(unittest.TestCase):
self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi') # field with non-alpha char self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi') # field with non-alpha char
self.assertRaises(ValueError, namedtuple, 'abc', 'abc class') # field has keyword self.assertRaises(ValueError, namedtuple, 'abc', 'abc class') # field has keyword
self.assertRaises(ValueError, namedtuple, 'abc', '8efg 9ghi') # field starts with digit self.assertRaises(ValueError, namedtuple, 'abc', '8efg 9ghi') # field starts with digit
self.assertRaises(ValueError, namedtuple, 'abc', '__efg__ ghi') # field with double underscores self.assertRaises(ValueError, namedtuple, 'abc', '_efg ghi') # field with leading underscore
self.assertRaises(ValueError, namedtuple, 'abc', 'efg efg ghi') # duplicate field self.assertRaises(ValueError, namedtuple, 'abc', 'efg efg ghi') # duplicate field
namedtuple('Point0', 'x1 y2') # Verify that numbers are allowed in names namedtuple('Point0', 'x1 y2') # Verify that numbers are allowed in names
namedtuple('_', '_ __ ___') # Verify that underscores are allowed namedtuple('_', 'a b c') # Test leading underscores in a typename
def test_instance(self): def test_instance(self):
Point = namedtuple('Point', 'x y') Point = namedtuple('Point', 'x y')
...@@ -46,17 +46,17 @@ class TestNamedTuple(unittest.TestCase): ...@@ -46,17 +46,17 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(repr(p), 'Point(x=11, y=22)') self.assertEqual(repr(p), 'Point(x=11, y=22)')
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 _asdict method
# Verify that __fields__ is read-only # Verify that _fields is read-only
try: try:
p.__fields__ = ('F1' ,'F2') p._fields = ('F1' ,'F2')
except AttributeError: except AttributeError:
pass pass
else: else:
self.fail('The __fields__ attribute needs to be read-only') self.fail('The _fields attribute needs to be read-only')
# verify that field string can have commas # verify that field string can have commas
Point = namedtuple('Point', 'x, y') Point = namedtuple('Point', 'x, y')
......
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