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/