Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 3c268be

Browse files
committed
Issue python#28556: allow default values in class form of NamedTuple -- Jelle Zijlstra
1 parent 37f183d commit 3c268be

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

Lib/test/test_typing.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,10 @@ class G(Generic[T]):
14001400
class CoolEmployee(NamedTuple):
14011401
name: str
14021402
cool: int
1403+
1404+
class CoolEmployeeWithDefault(NamedTuple):
1405+
name: str
1406+
cool: int = 0
14031407
"""
14041408

14051409
if PY36:
@@ -1959,6 +1963,28 @@ def test_annotation_usage(self):
19591963
collections.OrderedDict(name=str, cool=int))
19601964
self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__)
19611965

1966+
@skipUnless(PY36, 'Python 3.6 required')
1967+
def test_annotation_usage_with_default(self):
1968+
jelle = CoolEmployeeWithDefault('Jelle')
1969+
self.assertIsInstance(jelle, CoolEmployeeWithDefault)
1970+
self.assertIsInstance(jelle, tuple)
1971+
self.assertEqual(jelle.name, 'Jelle')
1972+
self.assertEqual(jelle.cool, 0)
1973+
cooler_employee = CoolEmployeeWithDefault('Sjoerd', 1)
1974+
self.assertEqual(cooler_employee.cool, 1)
1975+
1976+
self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault')
1977+
self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool'))
1978+
self.assertEqual(CoolEmployeeWithDefault._field_types, dict(name=str, cool=int))
1979+
self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0))
1980+
1981+
with self.assertRaises(TypeError):
1982+
exec("""
1983+
class NonDefaultAfterDefault(NamedTuple):
1984+
x: int = 3
1985+
y: int
1986+
""")
1987+
19621988
@skipUnless(PY36, 'Python 3.6 required')
19631989
def test_namedtuple_keyword_usage(self):
19641990
LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)

Lib/typing.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1959,7 +1959,22 @@ def __new__(cls, typename, bases, ns):
19591959
raise TypeError("Class syntax for NamedTuple is only supported"
19601960
" in Python 3.6+")
19611961
types = ns.get('__annotations__', {})
1962-
return _make_nmtuple(typename, types.items())
1962+
nm_tpl = _make_nmtuple(typename, types.items())
1963+
defaults = []
1964+
defaults_dict = {}
1965+
for field_name in types:
1966+
if field_name in ns:
1967+
default_value = ns[field_name]
1968+
defaults.append(default_value)
1969+
defaults_dict[field_name] = default_value
1970+
elif defaults:
1971+
raise TypeError("Non-default namedtuple field {field_name} cannot follow default"
1972+
" field(s) {default_names}"
1973+
.format(field_name=field_name,
1974+
default_names=', '.join(defaults_dict.keys())))
1975+
nm_tpl.__new__.__defaults__ = tuple(defaults)
1976+
nm_tpl._field_defaults = defaults_dict
1977+
return nm_tpl
19631978

19641979
class NamedTuple(metaclass=NamedTupleMeta):
19651980
"""Typed version of namedtuple.

0 commit comments

Comments
 (0)