On 3/13/2021 6:06 AM, Matt del Valle wrote:
<stuff deleted>
As I understand it your proposal would allow for the following, right?

import dataclasses


@dataclasses.dataclass class Parent: pos= dataclasses.POS_ARG
     a: int normal= dataclasses.NORMAL_ARG
     c: bool = False kw= dataclasses.KW_ARG
     e: list @dataclasses.dataclass class Child(Parent): pos= 
dataclasses.POS_ARG
     b: float = 3.14 normal= dataclasses.NORMAL_ARG
     d: dict = dataclasses.field(default_factory=dict) kw= dataclasses.KW_ARG
     f: set = dataclasses.field(default_factory=set)

Producing an __init__ like:

def __init__(self, a: int, b: float = 3.14, /, c: bool = False, d: dict = None, *, e: list, f: set = None): That is to say, you can specify these special values *once per class* in the inheritance hierarchy (not just once overall) and the fields in each category get added on at the end of that category (normal/pos/wk) compared to the parent's __init__ signature.

Yes, although:

- It needs to be "pos: dataclasses.POS_ARG" (colon not equals) in order for it to make it into __attributes__, which is what dataclass() uses for processing. - I don't think we'd want to restrict KW_ARG, POS_ARG, NORMAL_ARG to only once per class. After all, if you're using "field(kw_only=True)" and "field=(pos_only=True)" you could switch back and forth any number of times. I don't think this would be common, but I don't see a reason to prohibit it. Special rules, and all.

When processing your Parent class, dataclass() produces a dict of fields [*]:

{'a': Field(type=int, arg_type=POS_ARG, <other stuff>),
 'c': Field(type=bool, arg_type=NORMAL_ARG, default=False, <other stuff>),
 'e': Field(type=list, arg_type=KW_ARG, <other stuff>),
}

And for Child:
{'b': Field(type=float, arg_type=POS_ARG, default=3.14, <other stuff>),
 'd': Field(type=dict, arg_type=NORMAL_ARG, default_factory=dict, <other stuff>),
 'f': Field(type=set, arg_type=KW_ARG, default_factory=set, <other stuff>),
}

Note that your "normal", "pos", and "kw" members don't make it into the dict of fields in either of these classes. As I said in the first email, the only thing these markers are used for is to set the arg_type of each field. They're not retained anywhere (unless I don't delete them from __attributes__, which I haven't given any thought to).

Then it merges those (using whatever rules it has if it finds name collisions, which doesn't apply here) to come up with:
{'a': Field(type=int, arg_type=POS_ARG, <other stuff>),
 'c': Field(type=bool, arg_type=NORMAL_ARG, default=False, <other stuff>),
 'e': Field(type=list, arg_type=KW_ARG, <other stuff>),
 'b': Field(type=float, arg_type=POS_ARG, default=3.14, <other stuff>),
 'd': Field(type=dict, arg_type=NORMAL_ARG, default_factory=dict, <other stuff>),
 'f': Field(type=set, arg_type=KW_ARG, default_factory=set, <other stuff>),
}

Then it puts the POS_ARGs first, then NORMAL_ARGs, then KW_ARGs, to come up with:

def __init__(self, a:int, b:float=3.14, /, c:bool=False, d:dict=None, *, e:list, f:set=None):

(Except it does something more complicated with defaults, and especially default_factory's.)

I expect that

- the 80% use case will just be "@dataclasses.dataclass(kw_only=True)"
- if using a marker, "_: dataclasses.KW_ARG" will be the only one used, and anything else would be rare
- having all three markers will be very rare
- almost never would a marker be specified more than once

If so, I'm happy with this.

I'm glad. Thank you for your comments here, it made me write down what I'd been considering for inheritance.

Eric

[*]: I'm not committing to using arg_type in a Field object, it's just illustrative for this example. I will probably have two flags, kw_only and pos_only so that it matches the field() method.



PS:

I do think there will be some confusion around unexpected behaviour when someone who has seen this many times:

@dataclasses.dataclass class Example: _= dataclasses.KW_ARG
     a: str

tries this and is confused as to why it doesn't work as expected:

@dataclasses.dataclass class Example: _= dataclasses.POS_ARG
     a: str _= dataclasses.KW_ARG
     b: int
Basically, the requirement that the names these special values are assigned to must be unique is an unfortunate side-effect of the fact that Python doesn't bind free-floating objects to locals in some way, the way it does with for type hints into __annotations__ which would allow the much cleaner:
@dataclasses.dataclass class Example: dataclasses.POS_ARG
     a: str dataclasses.KW_ARG
     b: int
Come to think of it, this would make certain declarative constructs like those found in SQLAlchemy much cleaner as well, but that's a proposal for a different thread :p

On Sat, Mar 13, 2021 at 9:50 AM Eric V. Smith <e...@trueblade.com <mailto:e...@trueblade.com>> wrote:

    Sorry, Matt: I meant to credit you by name, but forgot to during
    my final edit.

    I still don't like the verbosity and the look of 'with' statements
    or classes. Or the fact that some of the fields are indented
    relative to others.

    And should also mention that Ethan Furman suggested using '*' and
    '/' as the "type", in
    
https://mail.python.org/archives/list/python-ideas@python.org/message/BIAVX4O6JRPQY7S3XG2IX6BSBZLAR2NS/
    
<https://mail.python.org/archives/list/python-ideas@python.org/message/BIAVX4O6JRPQY7S3XG2IX6BSBZLAR2NS/>
    , although the interaction with typing (specifically
    get_type_hints) might be an issue:

        class Hmm:
            #
            this: int
            that: float
            #
            pos: '/'
            #
            these: str
            those: str
            #
            key: '*'
            #
            some: list

    Anyway, Matt's and Ethan's proposal are on the other thread. I'd
    like to keep this thread focused on my proposal of
    dataclasses.KW_ONLY and .POS_ONLY. Not that saying "I'd like focus
    this thread" has ever worked in the history of python-ideas.

    Eric

    On 3/13/2021 2:40 AM, Matt Wozniski wrote:
    On Fri, Mar 12, 2021, 11:55 PM Eric V. Smith <e...@trueblade.com
    <mailto:e...@trueblade.com>> wrote:

        I should mention another idea that showed up on python-ideas, at
        
https://mail.python.org/archives/list/python-ideas@python.org/message/WBL4X46QG2HY5ZQWYVX4MXG5LK7QXBWB/
        
<https://mail.python.org/archives/list/python-ideas@python.org/message/WBL4X46QG2HY5ZQWYVX4MXG5LK7QXBWB/>

        . It would allow you to specify the flag via code like:

        @dataclasses.dataclass
        class Parent:
             with dataclasses.positional():
                 a: int
             c: bool = False
             with dataclasses.keyword():
                 e: list

        I'm not crazy about it, and it looks like it would require stack
        inspection to get it to work, but I mention it here for
        completeness.


    I think stack inspection could be avoided if we did something like:

    ```
    @dataclasses.dataclass
    class Parent:
         class pos(dataclasses.PositionalOnly):
             a: int
         c: bool = False
         class kw(dataclasses.KeywordOnly):
             e: list
    ```

    Like your proposal, the names for the two inner classes can be
    anything, but they must be unique. The metaclass would check if a
    field in the new class's namespace was a subclass of
    PositionalOnly or KeywordOnly, and if so recurse into its
    annotations to collect more fields.

    This still seems hacky, but it seems to read reasonably nicely,
    and behaves obviously in the presence of subclassing.

    _______________________________________________
    Python-ideas mailing list --python-ideas@python.org  
<mailto:python-ideas@python.org>
    To unsubscribe send an email topython-ideas-le...@python.org  
<mailto:python-ideas-le...@python.org>
    https://mail.python.org/mailman3/lists/python-ideas.python.org/  
<https://mail.python.org/mailman3/lists/python-ideas.python.org/>
    Message archived 
athttps://mail.python.org/archives/list/python-ideas@python.org/message/IFE35VNDZH5YUNXY23I53QBDCUFB7GRQ/
  
<https://mail.python.org/archives/list/python-ideas@python.org/message/IFE35VNDZH5YUNXY23I53QBDCUFB7GRQ/>
    Code of Conduct:http://python.org/psf/codeofconduct/  
<http://python.org/psf/codeofconduct/>

-- Eric V. Smith

    _______________________________________________
    Python-ideas mailing list -- python-ideas@python.org
    <mailto:python-ideas@python.org>
    To unsubscribe send an email to python-ideas-le...@python.org
    <mailto:python-ideas-le...@python.org>
    https://mail.python.org/mailman3/lists/python-ideas.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/4N2NOOXZBP5HF66N3OXD7CZ3HCGD3SLV/
    
<https://mail.python.org/archives/list/python-ideas@python.org/message/4N2NOOXZBP5HF66N3OXD7CZ3HCGD3SLV/>
    Code of Conduct: http://python.org/psf/codeofconduct/
    <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/CZV6IHHMIUJ3EOSWJAKGEX7ILM5UUMZF/
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/CHZS6ULWGSR3NQTLS2HGNIS24PGXFZEN/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to