[Python-checkins] cpython (merge 3.6 -> default): Issue #28556: allow default values in class form of NamedTuple -- Jelle

guido.van.rossum python-checkins at python.org
Wed Jan 18 11:05:12 EST 2017


https://hg.python.org/cpython/rev/f0e1c49233ff
changeset:   106223:f0e1c49233ff
parent:      106220:8cc5d78d9b18
parent:      106222:2159f36ccd6b
user:        Guido van Rossum <guido at python.org>
date:        Wed Jan 18 08:03:54 2017 -0800
summary:
  Issue #28556: allow default values in class form of NamedTuple -- Jelle Zijlstra (3.6->3.7)

files:
  Lib/test/test_typing.py |  26 ++++++++++++++++++++++++++
  Lib/typing.py           |  17 ++++++++++++++++-
  2 files changed, 42 insertions(+), 1 deletions(-)


diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -1400,6 +1400,10 @@
 class CoolEmployee(NamedTuple):
     name: str
     cool: int
+
+class CoolEmployeeWithDefault(NamedTuple):
+    name: str
+    cool: int = 0
 """
 
 if PY36:
@@ -1960,6 +1964,28 @@
         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')
     def test_namedtuple_keyword_usage(self):
         LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int)
         nick = LocalEmployee('Nick', 25)
diff --git a/Lib/typing.py b/Lib/typing.py
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -1959,7 +1959,22 @@
             raise TypeError("Class syntax for NamedTuple is only supported"
                             " in Python 3.6+")
         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):
     """Typed version of namedtuple.

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list