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/