That's fair. Let me then qualify my statement with "in the initial
release". The initial release has enough functionality to deal with without
considering your rather esoteric use case. (And I consider it esoteric
because attrs has apparently never seen the need to solve it either.) We
can reconsider for 3.8.


On Mon, Jan 29, 2018 at 11:38 AM, George Leslie-Waksman <waks...@gmail.com>
wrote:

> 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)
>>
>


-- 
--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