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 <e...@trueblade.com> 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 -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/XSAYT2MFOIBYHYINDHLPR7V2WHCWRYPE/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
>
>
> _______________________________________________
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to 
> python-ideas-leave@python.orghttps://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-ideas@python.org/message/UGNLUWT4OQC2JMEXSNIJRRCC4KMBE6XJ/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
> --
> Eric V. Smith
>
> _______________________________________________
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/6E6AI6GOEBS6XGUI5YFEO5JQ4N6RGLNE/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/2M7LYIFW3E3QSOHEPZYQP2M7LJGHGMUZ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to