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/