Given I started this thread from a perspective of this is a feature that I
would like because I need it, it feels a little dismissive to take attrs
not having the feature to mean "there's no reason to try to implement this."

On Mon, Jan 29, 2018 at 11:05 AM Guido van Rossum <gu...@python.org> wrote:

> I think that settles it -- there's no reason to try to implement this.
>
> On Mon, Jan 29, 2018 at 10:51 AM, George Leslie-Waksman <waks...@gmail.com
> > wrote:
>
>> attrs' seems to also not allow mandatory attributes to follow optional
>> one:
>>
>> In [14]: @attr.s
>>     ...: class Baz:
>>     ...:     a = attr.ib(default=attr.Factory(list))
>>     ...:     b = attr.ib()
>>     ...:
>>
>> ---------------------------------------------------------------------------
>> ValueError                                Traceback (most recent call
>> last)
>> <ipython-input-14-2c63f3f229a5> in <module>()
>> ----> 1 @attr.s
>>       2 class Baz:
>>       3     a = attr.ib(default=attr.Factory(list))
>>       4     b = attr.ib()
>>       5
>>
>> /Users/waksman/.pyenv/versions/3.6.1/envs/temp/lib/python3.6/site-packages/attr/_make.py
>> in attrs(maybe_cls, these, repr_ns, repr, cmp, hash, init, slots, frozen,
>> str, auto_attribs)
>>     700         return wrap
>>     701     else:
>> --> 702         return wrap(maybe_cls)
>>     703
>>     704
>>
>> /Users/waksman/.pyenv/versions/3.6.1/envs/temp/lib/python3.6/site-packages/attr/_make.py
>> in wrap(cls)
>>     669             raise TypeError("attrs only works with new-style
>> classes.")
>>     670
>> --> 671         builder = _ClassBuilder(cls, these, slots, frozen,
>> auto_attribs)
>>     672
>>     673         if repr is True:
>>
>> /Users/waksman/.pyenv/versions/3.6.1/envs/temp/lib/python3.6/site-packages/attr/_make.py
>> in __init__(self, cls, these, slots, frozen, auto_attribs)
>>     369
>>     370     def __init__(self, cls, these, slots, frozen, auto_attribs):
>> --> 371         attrs, super_attrs = _transform_attrs(cls, these,
>> auto_attribs)
>>     372
>>     373         self._cls = cls
>>
>> /Users/waksman/.pyenv/versions/3.6.1/envs/temp/lib/python3.6/site-packages/attr/_make.py
>> in _transform_attrs(cls, these, auto_attribs)
>>     335                 "No mandatory attributes allowed after an
>> attribute with a "
>>     336                 "default value or factory.  Attribute in
>> question: {a!r}"
>> --> 337                 .format(a=a)
>>     338             )
>>     339         elif had_default is False and \
>>
>> ValueError: No mandatory attributes allowed after an attribute with a
>> default value or factory.  Attribute in question: Attribute(name='b',
>> default=NOTHING, validator=None, repr=True, cmp=True, hash=None, init=True,
>> metadata=mappingproxy({}), type=None, converter=None)
>>
>>
>> On Fri, Jan 26, 2018 at 1:44 PM Guido van Rossum <gu...@python.org>
>> wrote:
>>
>>> What does attrs' solution for this problem look like?
>>>
>>> On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman <
>>> waks...@gmail.com> wrote:
>>>
>>>> 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.
>>>>
>>>> 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.
>>>>
>>>> How do I got about putting together a proposal to get this into 3.8?
>>>>
>>>> --George
>>>>
>>>>
>>>> On Thu, Jan 25, 2018 at 5:12 AM Eric V. Smith <e...@trueblade.com>
>>>> wrote:
>>>>
>>>>> I'm not completely opposed to this feature. But there are some cases to
>>>>> consider. Here's the first one that occurs to me: note that due to the
>>>>> way dataclasses work, it would need to be used everywhere down an
>>>>> inheritance hierarchy. That is, if an intermediate base class required
>>>>> it, all class derived from that intermediate base would need to specify
>>>>> it, too. That's because each class just makes decisions based on its
>>>>> fields and its base classes' fields, and not on any flags attached to
>>>>> the base class. As it's currently implemented, a class doesn't remember
>>>>> any of the decorator's arguments, so there's no way to look for this
>>>>> information, anyway.
>>>>>
>>>>> I think there are enough issues here that it's not going to make it in
>>>>> to 3.7. It would require getting a firm proposal together, selling the
>>>>> idea on python-dev, and completing the implementation before Monday.
>>>>> But
>>>>> if you want to try, I'd participate in the discussion.
>>>>>
>>>>> Taking Ivan's suggestion one step further, a way to do this currently
>>>>> is
>>>>> to pass init=False and then write another decorator that adds the
>>>>> kw-only __init__. So the usage would be:
>>>>>
>>>>> @dataclass
>>>>>      class Foo:
>>>>>          some_default: dict = field(default_factory=dict)
>>>>>
>>>>> @kw_only_init
>>>>> @dataclass(init=False)
>>>>> class Bar(Foo):
>>>>>      other_field: int
>>>>>
>>>>> kw_only_init(cls) would look at fields(cls) and construct the __init__.
>>>>> It would be a hassle to re-implement dataclasses's _init_fn function,
>>>>> but it could be made to work (in reality, of course, you'd just copy it
>>>>> and hack it up to do what you want). You'd also need to use some
>>>>> private
>>>>> knowledge of InitVars if you wanted to support them (the stock
>>>>> fields(cls) doesn't return them).
>>>>>
>>>>> For 3.8 we can consider changing dataclasses's APIs if we want to add
>>>>> this.
>>>>>
>>>>> Eric.
>>>>>
>>>>> On 1/25/2018 1:38 AM, George Leslie-Waksman wrote:
>>>>> > It may be possible but it makes for pretty leaky abstractions and
>>>>> it's
>>>>> > unclear what that custom __init__ should look like. How am I
>>>>> supposed to
>>>>> > know what the replacement for default_factory is?
>>>>> >
>>>>> > Moreover, suppose I want one base class with an optional argument
>>>>> and a
>>>>> > half dozen subclasses each with their own required argument. At that
>>>>> > point, I have to write the same __init__ function a half dozen times.
>>>>> >
>>>>> > It feels rather burdensome for the user when an additional flag (say
>>>>> > "kw_only=True") and a modification to:
>>>>> >
>>>>> https://github.com/python/cpython/blob/master/Lib/dataclasses.py#L294
>>>>>  that
>>>>> > inserted `['*']` after `[self_name]` if the flag is specified could
>>>>> > ameliorate this entire issue.
>>>>> >
>>>>> > On Wed, Jan 24, 2018 at 3:22 PM Ivan Levkivskyi <
>>>>> levkivs...@gmail.com
>>>>> > <mailto:levkivs...@gmail.com>> wrote:
>>>>> >
>>>>> >     It is possible to pass init=False to the decorator on the
>>>>> subclass
>>>>> >     (and supply your own custom __init__, if necessary):
>>>>> >
>>>>> >     @dataclass
>>>>> >     class Foo:
>>>>> >          some_default: dict = field(default_factory=dict)
>>>>> >
>>>>> >     @dataclass(init=False) # This works
>>>>> >     class Bar(Foo):
>>>>> >          other_field: int
>>>>> >
>>>>> >     --
>>>>> >     Ivan
>>>>> >
>>>>> >
>>>>> >
>>>>> >     On 23 January 2018 at 03:33, George Leslie-Waksman
>>>>> >     <waks...@gmail.com <mailto:waks...@gmail.com>> wrote:
>>>>> >
>>>>> >         The proposed implementation of dataclasses prevents defining
>>>>> >         fields with defaults before fields without defaults. This can
>>>>> >         create limitations on logical grouping of fields and on
>>>>> inheritance.
>>>>> >
>>>>> >         Take, for example, the case:
>>>>> >
>>>>> >         @dataclass
>>>>> >         class Foo:
>>>>> >              some_default: dict = field(default_factory=dict)
>>>>> >
>>>>> >         @dataclass
>>>>> >         class Bar(Foo):
>>>>> >              other_field: int
>>>>> >
>>>>> >         this results in the error:
>>>>> >
>>>>> >                5 @dataclass
>>>>> >         ----> 6 class Bar(Foo):
>>>>> >                7     other_field: int
>>>>> >                8
>>>>> >
>>>>> >
>>>>>  
>>>>> ~/.pyenv/versions/3.6.2/envs/clover_pipeline/lib/python3.6/site-packages/dataclasses.py
>>>>> >         in dataclass(_cls, init, repr, eq, order, hash, frozen)
>>>>> >              751
>>>>> >              752     # We're called as @dataclass, with a class.
>>>>> >         --> 753     return wrap(_cls)
>>>>> >              754
>>>>> >              755
>>>>> >
>>>>> >
>>>>>  
>>>>> ~/.pyenv/versions/3.6.2/envs/clover_pipeline/lib/python3.6/site-packages/dataclasses.py
>>>>> >         in wrap(cls)
>>>>> >              743
>>>>> >              744     def wrap(cls):
>>>>> >         --> 745         return _process_class(cls, repr, eq, order,
>>>>> >         hash, init, frozen)
>>>>> >              746
>>>>> >              747     # See if we're being called as @dataclass or
>>>>> >         @dataclass().
>>>>> >
>>>>> >
>>>>>  
>>>>> ~/.pyenv/versions/3.6.2/envs/clover_pipeline/lib/python3.6/site-packages/dataclasses.py
>>>>> >         in _process_class(cls, repr, eq, order, hash, init, frozen)
>>>>> >              675                                 #  in __init__.  Use
>>>>> >         "self" if possible.
>>>>> >              676
>>>>>  '__dataclass_self__' if
>>>>> >         'self' in fields
>>>>> >         --> 677                                     else 'self',
>>>>> >              678                                 ))
>>>>> >              679     if repr:
>>>>> >
>>>>> >
>>>>>  
>>>>> ~/.pyenv/versions/3.6.2/envs/clover_pipeline/lib/python3.6/site-packages/dataclasses.py
>>>>> >         in _init_fn(fields, frozen, has_post_init, self_name)
>>>>> >              422                 seen_default = True
>>>>> >              423             elif seen_default:
>>>>> >         --> 424                 raise TypeError(f'non-default
>>>>> argument
>>>>> >         {f.name <http://f.name>!r} '
>>>>> >              425                                 'follows default
>>>>> argument')
>>>>> >              426
>>>>> >
>>>>> >         TypeError: non-default argument 'other_field' follows default
>>>>> >         argument
>>>>> >
>>>>> >         I understand that this is a limitation of positional
>>>>> arguments
>>>>> >         because the effective __init__ signature is:
>>>>> >
>>>>> >         def __init__(self, some_default: dict = <something>,
>>>>> >         other_field: int):
>>>>> >
>>>>> >         However, keyword only arguments allow an entirely reasonable
>>>>> >         solution to this problem:
>>>>> >
>>>>> >         def __init__(self, *, some_default: dict = <something>,
>>>>> >         other_field: int):
>>>>> >
>>>>> >         And have the added benefit of making the fields in the
>>>>> __init__
>>>>> >         call entirely explicit.
>>>>> >
>>>>> >         So, I propose the addition of a keyword_only flag to the
>>>>> >         @dataclass decorator that renders the __init__ method using
>>>>> >         keyword only arguments:
>>>>> >
>>>>> >         @dataclass(keyword_only=True)
>>>>> >         class Bar(Foo):
>>>>> >              other_field: int
>>>>> >
>>>>> >         --George Leslie-Waksman
>>>>> >
>>>>> >         _______________________________________________
>>>>> >         Python-ideas mailing list
>>>>> >         Python-ideas@python.org <mailto:Python-ideas@python.org>
>>>>> >         https://mail.python.org/mailman/listinfo/python-ideas
>>>>> >         Code of Conduct: http://python.org/psf/codeofconduct/
>>>>> >
>>>>> >
>>>>> >
>>>>> >
>>>>> > _______________________________________________
>>>>> > Python-ideas mailing list
>>>>> > Python-ideas@python.org
>>>>> > https://mail.python.org/mailman/listinfo/python-ideas
>>>>> > Code of Conduct: http://python.org/psf/codeofconduct/
>>>>> >
>>>>>
>>>>>
>>>> _______________________________________________
>>>> Python-ideas mailing list
>>>> Python-ideas@python.org
>>>> https://mail.python.org/mailman/listinfo/python-ideas
>>>> Code of Conduct: http://python.org/psf/codeofconduct/
>>>>
>>>>
>>>
>>>
>>> --
>>> --Guido van Rossum (python.org/~guido)
>>>
>>
>
>
> --
> --Guido van Rossum (python.org/~guido)
>
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to