Disclaimer: I posted this earlier today but I think due to some first-post moderation related issues (that I've hopefully now gotten sorted out!) it may not have gone through. I'm posting this again just in case. If it's gone through and you've already seen it then I'm super sorry, please just ignore this.
If something like what you're suggesting
were to be implemented I would much rather it be done with context
managers than position-dependent special values, because otherwise you once again end up in a situation where
it's impossible to easily subclass a dataclass (which was one of the primary reasons this conversation even got started in the first place). So, for example:
import dataclasses
@dataclasses.dataclass
class SomeClass:
c: bool = False
# a normal field with a default value does not
# prevent subsequent positional fields from
# having no default value (such as 'a' below)
# however, all further normal fields now must
# specify a default value (such as 'd' below)
with dataclasses.positional():
a: int
b: float = 3.14
# once a positional field with a default value shows up
# all further positional fields and ALL normal fields
# (even retroactively!) must also specify defaults
# (for example, field 'c' above is
# now forced to specify a default value)
with dataclasses.keyword():
e: list
f: set = dataclasses.field(default_factory=set)
# once a keyword field with a default value shows up
# all further keyword fields must also specify defaults
d: dict = dataclasses.field(default_factory=dict)
# This ordering is clearly insane, but the essential
# point is that it works even with weird ordering
# which is necessary for it to work when subclassing
# where the order will almost always be wonky
#
# A sane version of the above would be:
@dataclasses.dataclass
class SomeClass:
with dataclasses.positional():
a: int
b: float = 3.14
c: bool = False
d: dict = dataclasses.field(default_factory=dict)
with dataclasses.keyword():
e: list
f: set = dataclasses.field(default_factory=set)
# either of the above will generate an __init__ like:
def __init__(self, a: int, b: float = 3.14,
/, c: bool = False, d: dict = None,
*, e: list, f: set = None):
self.a = a
self.b = b
self.c = c
self.d = dict() if d is None else d
self.e = e
self.f = set() if f is None else f
# parameters are arranged in order as
# positional -> normal -> keyword
# within the order they were defined in each
# individual category, but not necessarily
# whatever order they were defined in overall
#
# This is subclass-friendly!
#
# it should hopefully be obvious that we could
# have cut this class in half at literally any
# point (as long as the the parent class has
# the earlier arguments within each category)
# and put the rest into a child class and
# it would still have worked and generated the
# same __init__ signature
#
# For example:
@dataclasses.dataclass
class Parent:
with dataclasses.positional():
a: int
c: bool = False
with dataclasses.keyword():
e: list
@dataclasses.dataclass
class Child(Parent):
with dataclasses.positional():
b: float = 3.14
d: dict = dataclasses.field(default_factory=dict)
with dataclasses.keyword():
f: set = dataclasses.field(default_factory=set)
# Child now has the same __init__ signature as
# SomeClass above
Honestly, the more I think about it, the more I love the idea of something like this (even if it's not
exactly the same as my suggestion). Right now
dataclasses
do not support the full range of __init__ signatures you could generate
with a normal class, and they are extremely hostile to subclassing. That is a failing that often forces people to fall back to normal
classes in otherwise ideal
dataclass use-case situations.