Skip to content

Commit 27553f5

Browse files
committed
Allow dataclass subclasses to define required fields after optional base fields
1 parent b739ec5 commit 27553f5

2 files changed

Lines changed: 40 additions & 5 deletions

File tree

Lib/dataclasses.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -409,12 +409,21 @@ def field(*, default=MISSING, default_factory=MISSING, init=True, repr=True,
409409

410410

411411
def _fields_in_init_order(fields):
412-
# Returns the fields as __init__ will output them. It returns 2 tuples:
413-
# the first for normal args, and the second for keyword args.
412+
required_fields = []
413+
optional_fields = []
414+
kw_fields = []
415+
for f in fields:
416+
if not f.init:
417+
continue
418+
if f.kw_only:
419+
kw_fields.append(f)
420+
elif f.default is MISSING and f.default_factory is MISSING:
421+
required_fields.append(f)
422+
else:
423+
optional_fields.append(f)
424+
425+
return tuple(required_fields + optional_fields), tuple(kw_fields)
414426

415-
return (tuple(f for f in fields if f.init and not f.kw_only),
416-
tuple(f for f in fields if f.init and f.kw_only)
417-
)
418427

419428

420429
def _tuple_str(obj_name, fields):

Lib/test/test_dataclass.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import unittest
2+
from dataclasses import dataclass
3+
4+
class TestDataclassInheritanceFieldOrder(unittest.TestCase):
5+
def test_required_after_optional_in_subclass(self):
6+
@dataclass
7+
class Base:
8+
x: int = 10 # optional field with default
9+
10+
@dataclass
11+
class Sub(Base):
12+
y: int # required field in subclass
13+
14+
# Should require y but allow optional x to be omitted
15+
obj = Sub(y=5)
16+
self.assertEqual(obj.x, 10)
17+
self.assertEqual(obj.y, 5)
18+
19+
# Should allow overriding x
20+
obj2 = Sub(x=42, y=9)
21+
self.assertEqual(obj2.x, 42)
22+
self.assertEqual(obj2.y, 9)
23+
24+
# Missing y should raise TypeError
25+
with self.assertRaises(TypeError):
26+
Sub()

0 commit comments

Comments
 (0)