In response to Eric V. Smith, if something like what you're suggesting
were to be implemented I would much rather it be done with context managers
than 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 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:
c: bool = False
with dataclasses.keyword():
e: list
with dataclasses.positional():
a: int
@dataclasses.dataclass
class Child(Parent):
with dataclasses.keyword():
f: set = dataclasses.field(default_factory=set)
d: dict = dataclasses.field(default_factory=dict)
with dataclasses.positional():
b: float = 3.14
# we have shuffled around the ordering of the
# context managers and normal fields in both
# classes and it STILL works unambiguously!
Honestly, the more I think about it the more I'm +1 on something like this
(even if it's not *exactly* my suggestion). Right now dataclasses do not
support the full range of __init__ signatures you could generate with a
normal class (and are extremely hostile to subclassing), and that is a
failing that often forces people to fall back to normal classes in
otherwise ideal dataclass use-case situations.
On Thu, Mar 11, 2021 at 7:35 AM Eric V. Smith <[email protected]> wrote:
> On 3/11/2021 1:41 AM, Paul Bryan wrote:
>
> In my experience, a dataclass with more than a few attributes makes using
> positional arguments unwieldy and error-prone.
>
> Agreed, just like any function or class.
>
> I would think something like @dataclass(kwonly=*bool*) with default of
> False would be reasonably clean to implement and understand.
>
> Yes, I think that's a reasonable thing to do. But I don't want it to be
> the only option, I'd like to be able to mix and match some "normal"
> arguments and some keyword-only (and some positional-only).
>
>
> While I appreciate supporting existing behavior for backward
> compatibility, I'm not so clear on the value of supporting a hybrid of
> positional and keyword __init__ arguments. Could you shed some light on
> your reasoning for supporting it?
>
> The same as any function or class. From PEP 3102:
>
> def compare(a, b, *, key=None):
>
> This seems like a reasonable thing to want a dataclass to represent. Using
> my off-the-cuff proposal from below:
>
> @dataclasses.dataclass
> class Comparator:
> a: Any
> b: Any
> _: dataclasses.KEYWORD_ONLY
> key: Optional[Callable[whatever]] = None
>
> I don't want to restrict dataclasses: I'd like the full range of argument
> types to be available. This is especially true as dataclasses are used for
> more and more things (at least that's what happens in my code).
>
> Eric
>
>
>
> On Thu, 2021-03-11 at 00:47 -0500, Eric V. Smith wrote:
>
> As I've said before, I'm not opposed to the feature of keyword-only
> arguments. I think it would be a great addition.
>
> However, the proposal from Laurie O is only for changing fields without
> default values following fields with default values to be keyword-only. At
> least that's how I understand it.
>
> So, that means that:
>
> @dataclasses.dataclass
> class Point:
> x: int = 0
> y: int
> z: int
> t: int = 0
>
> Would generate a __init__ with this signature:
>
> def __init__(self, x=0, *, y, z, t=0):
>
> While it's an interesting application, I think that's just too limiting.
> Among other things, I can't define a dataclass where all fields are
> keyword-only, or a class where there's only a single field and it's
> keyword-only. I also have to have at least one keyword-only field (here, y)
> that has no default value. z and t can have defaults, but not y.
>
> What I'd like to see is some way of having keyword-only arguments, with or
> without defaults. And I'd also like to see if we could get support for
> positional-only arguments at the same time.
>
> I'm not sure of the best way to achieve this. Using flags to field()
> doesn't sound awesome, but could be made to work. Or maybe special field
> names or types? I'm not crazy about that, but using special types would let
> you do something like:
>
> @dataclasses.dataclass
> class Point:
> x: int = 0
> _: dataclasses.KEYWORD_ONLY
> y: int
> z: int
> t: int = 0
>
> And the dataclasses machinery would ignore the "_" field except for making
> everything after it keyword-only. Here the name "_" isn't special: any
> field (of any name) that's of type dataclasses.KEYWORD_ONLY would be
> ignored except for the keyword-only changing behavior. In some ways, it's
> like dataclasses.ClassVar, where the type is treated specially and the
> field doesn't become a __init__ argument.
>
> There are also issues with inheritance that would need to be thought
> through. This idea could also be extended for positional-only.
>
> I'm open to other suggestions.
>
> Eric
> On 3/10/2021 10:22 AM, Guido van Rossum wrote:
>
> _______________________________________________
> Python-ideas mailing list --
> To unsubscribe send an email to
>
> Message archived at
> Code of Conduct:
>
> --
>
> Eric V. Smith
>
> _______________________________________________
> Python-ideas mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/[email protected]/message/XSAYT2MFOIBYHYINDHLPR7V2WHCWRYPE/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
>
>
> _______________________________________________
> Python-ideas mailing list -- [email protected]
> To unsubscribe send an email to
> [email protected]https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/[email protected]/message/UGNLUWT4OQC2JMEXSNIJRRCC4KMBE6XJ/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
> --
> Eric V. Smith
>
> _______________________________________________
> Python-ideas mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/[email protected]/message/6E6AI6GOEBS6XGUI5YFEO5JQ4N6RGLNE/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/2M7LYIFW3E3QSOHEPZYQP2M7LJGHGMUZ/
Code of Conduct: http://python.org/psf/codeofconduct/