<div dir="ltr">What does attrs' solution for this problem look like?<br></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman <span dir="ltr"><<a href="mailto:waksman@gmail.com" target="_blank">waksman@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Even if we could inherit the setting, I would think that we would still want to require the code be explicit. It seems worse to implicitly require keyword only arguments for a class without giving any indication in the code.<div><br></div><div>As it stands, the current implementation does not allow a later subclass to be declared without `keyword_only=True` so we could handle this case by adding a note to the `TypeError` message about considering the keyword_only flag.</div><div><br></div><div>How do I got about putting together a proposal to get this into 3.8?<span class="HOEnZb"><font color="#888888"><br><br>--George</font></span><div><div class="h5"><br><br><div class="gmail_quote"><div dir="ltr">On Thu, Jan 25, 2018 at 5:12 AM Eric V. Smith <<a href="mailto:eric@trueblade.com" target="_blank">eric@trueblade.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I'm not completely opposed to this feature. But there are some cases to<br>
consider. Here's the first one that occurs to me: note that due to the<br>
way dataclasses work, it would need to be used everywhere down an<br>
inheritance hierarchy. That is, if an intermediate base class required<br>
it, all class derived from that intermediate base would need to specify<br>
it, too. That's because each class just makes decisions based on its<br>
fields and its base classes' fields, and not on any flags attached to<br>
the base class. As it's currently implemented, a class doesn't remember<br>
any of the decorator's arguments, so there's no way to look for this<br>
information, anyway.<br>
<br>
I think there are enough issues here that it's not going to make it in<br>
to 3.7. It would require getting a firm proposal together, selling the<br>
idea on python-dev, and completing the implementation before Monday. But<br>
if you want to try, I'd participate in the discussion.<br>
<br>
Taking Ivan's suggestion one step further, a way to do this currently is<br>
to pass init=False and then write another decorator that adds the<br>
kw-only __init__. So the usage would be:<br>
<br>
@dataclass<br>
     class Foo:<br>
         some_default: dict = field(default_factory=dict)<br>
<br>
@kw_only_init<br>
@dataclass(init=False)<br>
class Bar(Foo):<br>
     other_field: int<br>
<br>
kw_only_init(cls) would look at fields(cls) and construct the __init__.<br>
It would be a hassle to re-implement dataclasses's _init_fn function,<br>
but it could be made to work (in reality, of course, you'd just copy it<br>
and hack it up to do what you want). You'd also need to use some private<br>
knowledge of InitVars if you wanted to support them (the stock<br>
fields(cls) doesn't return them).<br>
<br>
For 3.8 we can consider changing dataclasses's APIs if we want to add this.<br>
<br>
Eric.<br>
<br>
On 1/25/2018 1:38 AM, George Leslie-Waksman wrote:<br>
> It may be possible but it makes for pretty leaky abstractions and it's<br>
> unclear what that custom __init__ should look like. How am I supposed to<br>
> know what the replacement for default_factory is?<br>
><br>
> Moreover, suppose I want one base class with an optional argument and a<br>
> half dozen subclasses each with their own required argument. At that<br>
> point, I have to write the same __init__ function a half dozen times.<br>
><br>
> It feels rather burdensome for the user when an additional flag (say<br>
> "kw_only=True") and a modification to:<br>
> <a href="https://github.com/python/cpython/blob/master/Lib/dataclasses.py#L294" rel="noreferrer" target="_blank">https://github.com/python/<wbr>cpython/blob/master/Lib/<wbr>dataclasses.py#L294</a> that<br>
> inserted `['*']` after `[self_name]` if the flag is specified could<br>
> ameliorate this entire issue.<br>
><br>
> On Wed, Jan 24, 2018 at 3:22 PM Ivan Levkivskyi <<a href="mailto:levkivskyi@gmail.com" target="_blank">levkivskyi@gmail.com</a><br>
> <mailto:<a href="mailto:levkivskyi@gmail.com" target="_blank">levkivskyi@gmail.com</a>>> wrote:<br>
><br>
>     It is possible to pass init=False to the decorator on the subclass<br>
>     (and supply your own custom __init__, if necessary):<br>
><br>
>     @dataclass<br>
>     class Foo:<br>
>          some_default: dict = field(default_factory=dict)<br>
><br>
>     @dataclass(init=False) # This works<br>
>     class Bar(Foo):<br>
>          other_field: int<br>
><br>
>     --<br>
>     Ivan<br>
><br>
><br>
><br>
>     On 23 January 2018 at 03:33, George Leslie-Waksman<br>
>     <<a href="mailto:waksman@gmail.com" target="_blank">waksman@gmail.com</a> <mailto:<a href="mailto:waksman@gmail.com" target="_blank">waksman@gmail.com</a>>> wrote:<br>
><br>
>         The proposed implementation of dataclasses prevents defining<br>
>         fields with defaults before fields without defaults. This can<br>
>         create limitations on logical grouping of fields and on inheritance.<br>
><br>
>         Take, for example, the case:<br>
><br>
>         @dataclass<br>
>         class Foo:<br>
>              some_default: dict = field(default_factory=dict)<br>
><br>
>         @dataclass<br>
>         class Bar(Foo):<br>
>              other_field: int<br>
><br>
>         this results in the error:<br>
><br>
>                5 @dataclass<br>
>         ----> 6 class Bar(Foo):<br>
>                7     other_field: int<br>
>                8<br>
><br>
>         ~/.pyenv/versions/3.6.2/envs/<wbr>clover_pipeline/lib/python3.6/<wbr>site-packages/dataclasses.py<br>
>         in dataclass(_cls, init, repr, eq, order, hash, frozen)<br>
>              751<br>
>              752     # We're called as @dataclass, with a class.<br>
>         --> 753     return wrap(_cls)<br>
>              754<br>
>              755<br>
><br>
>         ~/.pyenv/versions/3.6.2/envs/<wbr>clover_pipeline/lib/python3.6/<wbr>site-packages/dataclasses.py<br>
>         in wrap(cls)<br>
>              743<br>
>              744     def wrap(cls):<br>
>         --> 745         return _process_class(cls, repr, eq, order,<br>
>         hash, init, frozen)<br>
>              746<br>
>              747     # See if we're being called as @dataclass or<br>
>         @dataclass().<br>
><br>
>         ~/.pyenv/versions/3.6.2/envs/<wbr>clover_pipeline/lib/python3.6/<wbr>site-packages/dataclasses.py<br>
>         in _process_class(cls, repr, eq, order, hash, init, frozen)<br>
>              675                                 #  in __init__.  Use<br>
>         "self" if possible.<br>
>              676                                 '__dataclass_self__' if<br>
>         'self' in fields<br>
>         --> 677                                     else 'self',<br>
>              678                                 ))<br>
>              679     if repr:<br>
><br>
>         ~/.pyenv/versions/3.6.2/envs/<wbr>clover_pipeline/lib/python3.6/<wbr>site-packages/dataclasses.py<br>
>         in _init_fn(fields, frozen, has_post_init, self_name)<br>
>              422                 seen_default = True<br>
>              423             elif seen_default:<br>
>         --> 424                 raise TypeError(f'non-default argument<br>
>         {<a href="http://f.name" rel="noreferrer" target="_blank">f.name</a> <<a href="http://f.name" rel="noreferrer" target="_blank">http://f.name</a>>!r} '<br>
>              425                                 'follows default argument')<br>
>              426<br>
><br>
>         TypeError: non-default argument 'other_field' follows default<br>
>         argument<br>
><br>
>         I understand that this is a limitation of positional arguments<br>
>         because the effective __init__ signature is:<br>
><br>
>         def __init__(self, some_default: dict = <something>,<br>
>         other_field: int):<br>
><br>
>         However, keyword only arguments allow an entirely reasonable<br>
>         solution to this problem:<br>
><br>
>         def __init__(self, *, some_default: dict = <something>,<br>
>         other_field: int):<br>
><br>
>         And have the added benefit of making the fields in the __init__<br>
>         call entirely explicit.<br>
><br>
>         So, I propose the addition of a keyword_only flag to the<br>
>         @dataclass decorator that renders the __init__ method using<br>
>         keyword only arguments:<br>
><br>
>         @dataclass(keyword_only=True)<br>
>         class Bar(Foo):<br>
>              other_field: int<br>
><br>
>         --George Leslie-Waksman<br>
><br>
>         _____________________________<wbr>__________________<br>
>         Python-ideas mailing list<br>
>         <a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a> <mailto:<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.<wbr>org</a>><br>
>         <a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-ideas</a><br>
>         Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/<wbr>codeofconduct/</a><br>
><br>
><br>
><br>
><br>
> ______________________________<wbr>_________________<br>
> Python-ideas mailing list<br>
> <a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
> <a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-ideas</a><br>
> Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/<wbr>codeofconduct/</a><br>
><br>
<br>
</blockquote></div></div></div></div></div>
<br>______________________________<wbr>_________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/<wbr>codeofconduct/</a><br>
<br></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div>