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)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/