Issue #28556: allow default values in class form of NamedTuple -- Jelle Zijlstra
This commit is contained in:
parent
37f183d43d
commit
3c268be885
|
@ -1400,6 +1400,10 @@ class G(Generic[T]):
|
||||||
class CoolEmployee(NamedTuple):
|
class CoolEmployee(NamedTuple):
|
||||||
name: str
|
name: str
|
||||||
cool: int
|
cool: int
|
||||||
|
|
||||||
|
class CoolEmployeeWithDefault(NamedTuple):
|
||||||
|
name: str
|
||||||
|
cool: int = 0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if PY36:
|
if PY36:
|
||||||
|
@ -1959,6 +1963,28 @@ class NamedTupleTests(BaseTestCase):
|
||||||
collections.OrderedDict(name=str, cool=int))
|
collections.OrderedDict(name=str, cool=int))
|
||||||
self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__)
|
self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__)
|
||||||
|
|
||||||
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
|
def test_annotation_usage_with_default(self):
|
||||||
|
jelle = CoolEmployeeWithDefault('Jelle')
|
||||||
|
self.assertIsInstance(jelle, CoolEmployeeWithDefault)
|
||||||
|
self.assertIsInstance(jelle, tuple)
|
||||||
|
self.assertEqual(jelle.name, 'Jelle')
|
||||||
|
self.assertEqual(jelle.cool, 0)
|
||||||
|
cooler_employee = CoolEmployeeWithDefault('Sjoerd', 1)
|
||||||
|
self.assertEqual(cooler_employee.cool, 1)
|
||||||
|
|
||||||
|
self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault')
|
||||||
|
self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool'))
|
||||||
|
self.assertEqual(CoolEmployeeWithDefault._field_types, dict(name=str, cool=int))
|
||||||
|
self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0))
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
exec("""
|
||||||
|
class NonDefaultAfterDefault(NamedTuple):
|
||||||
|
x: int = 3
|
||||||
|
y: int
|
||||||
|
""")
|
||||||
|
|
||||||
@skipUnless(PY36, 'Python 3.6 required')
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
def test_namedtuple_keyword_usage(self):
|
def test_namedtuple_keyword_usage(self):
|
||||||
LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
|
LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
|
||||||
|
|
|
@ -1959,7 +1959,22 @@ class NamedTupleMeta(type):
|
||||||
raise TypeError("Class syntax for NamedTuple is only supported"
|
raise TypeError("Class syntax for NamedTuple is only supported"
|
||||||
" in Python 3.6+")
|
" in Python 3.6+")
|
||||||
types = ns.get('__annotations__', {})
|
types = ns.get('__annotations__', {})
|
||||||
return _make_nmtuple(typename, types.items())
|
nm_tpl = _make_nmtuple(typename, types.items())
|
||||||
|
defaults = []
|
||||||
|
defaults_dict = {}
|
||||||
|
for field_name in types:
|
||||||
|
if field_name in ns:
|
||||||
|
default_value = ns[field_name]
|
||||||
|
defaults.append(default_value)
|
||||||
|
defaults_dict[field_name] = default_value
|
||||||
|
elif defaults:
|
||||||
|
raise TypeError("Non-default namedtuple field {field_name} cannot follow default"
|
||||||
|
" field(s) {default_names}"
|
||||||
|
.format(field_name=field_name,
|
||||||
|
default_names=', '.join(defaults_dict.keys())))
|
||||||
|
nm_tpl.__new__.__defaults__ = tuple(defaults)
|
||||||
|
nm_tpl._field_defaults = defaults_dict
|
||||||
|
return nm_tpl
|
||||||
|
|
||||||
class NamedTuple(metaclass=NamedTupleMeta):
|
class NamedTuple(metaclass=NamedTupleMeta):
|
||||||
"""Typed version of namedtuple.
|
"""Typed version of namedtuple.
|
||||||
|
|
Loading…
Reference in New Issue