Dataclasses, keyword args, and inheritance
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!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
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 <waksman@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!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 https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
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 <levkivskyi@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 <waksman@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!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 https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
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 <levkivskyi@gmail.com <mailto:levkivskyi@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 <waksman@gmail.com <mailto:waksman@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/
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 <eric@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.
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
On 1/25/2018 1:38 AM, George Leslie-Waksman wrote: 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 <levkivskyi@gmail.com <mailto:levkivskyi@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 <waksman@gmail.com <mailto:waksman@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/
What does attrs' solution for this problem look like? On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman <waksman@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 <eric@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.
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
On 1/25/2018 1:38 AM, George Leslie-Waksman wrote: 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 <levkivskyi@gmail.com <mailto:levkivskyi@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 <waksman@gmail.com <mailto:waksman@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)
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 <guido@python.org> wrote:
What does attrs' solution for this problem look like?
On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman <waksman@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 <eric@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.
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
On 1/25/2018 1:38 AM, George Leslie-Waksman wrote: 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 <levkivskyi@gmail.com <mailto:levkivskyi@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 <waksman@gmail.com <mailto:waksman@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)
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 <waksman@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 <guido@python.org> wrote:
What does attrs' solution for this problem look like?
On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman < waksman@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 <eric@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.
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
On 1/25/2018 1:38 AM, George Leslie-Waksman wrote: 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 <levkivskyi@gmail.com <mailto:levkivskyi@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 <waksman@gmail.com <mailto:waksman@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)
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 <guido@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 <waksman@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 <guido@python.org> wrote:
What does attrs' solution for this problem look like?
On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman < waksman@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 <eric@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 < levkivskyi@gmail.com <mailto:levkivskyi@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 <waksman@gmail.com <mailto:waksman@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)
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 <waksman@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 <guido@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 < waksman@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 <guido@python.org> wrote:
What does attrs' solution for this problem look like?
On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman < waksman@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 <eric@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 < levkivskyi@gmail.com > <mailto:levkivskyi@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 > <waksman@gmail.com <mailto:waksman@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)
George For what’s worth if it can help for your need – I know that this is not part of the dataclasses PEP – inheritance now works with @autoclass (https://smarie.github.io/python-autoclass/ ), as demonstrated below. Note that unfortunately for mutable default field values you still have to perform self-assignment. But that’s the same problem that you have with ‘standard’ python. # with an unmutable default value from autoclass import autoclass @autoclass class Foo: def __init__(self, some_default: str = 'unmutable_default'): pass @autoclass class Bar(Foo): def __init__(self, other_field: int, some_default: str = 'unmutable_default'): super(Bar, self).__init__(some_default=some_default) a = Bar(2) assert a.other_field == 2 assert a.some_default == 'unmutable_default' # with a mutable default value @autoclass class Foo: def __init__(self, some_default: str = None): # you have to assign, you can not delegate to @autoclass for this :( self.some_default = some_default or ['mutable_default'] @autoclass class Bar(Foo): def __init__(self, other_field: int, some_default: str = None): super(Bar, self).__init__(some_default=some_default) a = Bar(2) assert a.other_field == 2 assert a.some_default == ['mutable_default'] By the way is there any plan or idea to allow users to provide “default values factories (generators)” directly in the function signature, when their default value is mutable ? That could be a great improvement of the python language. I could not find anything googling around… let me know if I should create a dedicated thread to discuss this. Kind regards Sylvain De : Python-ideas [mailto:python-ideas-bounces+sylvain.marie=schneider-electric.com@python.org] De la part de Guido van Rossum Envoyé : lundi 29 janvier 2018 20:44 À : George Leslie-Waksman <waksman@gmail.com> Cc : Eric V. Smith <eric@trueblade.com>; python-ideas <python-ideas@python.org> Objet : Re: [Python-ideas] Dataclasses, keyword args, and inheritance 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 <waksman@gmail.com<mailto:waksman@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 <guido@python.org<mailto:guido@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 <waksman@gmail.com<mailto:waksman@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 <guido@python.org<mailto:guido@python.org>> wrote: What does attrs' solution for this problem look like? On Fri, Jan 26, 2018 at 11:11 AM, George Leslie-Waksman <waksman@gmail.com<mailto:waksman@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 <eric@trueblade.com<mailto:eric@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 <levkivskyi@gmail.com<mailto:levkivskyi@gmail.com> <mailto:levkivskyi@gmail.com<mailto:levkivskyi@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 <waksman@gmail.com<mailto:waksman@gmail.com> <mailto:waksman@gmail.com<mailto:waksman@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> <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> <mailto: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<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<mailto: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<http://python.org/~guido>) -- --Guido van Rossum (python.org/~guido<http://python.org/~guido>) -- --Guido van Rossum (python.org/~guido<http://python.org/~guido>) ______________________________________________________________________ This email has been scanned by the Symantec Email Security.cloud service. ______________________________________________________________________
It's been two years since this proposal has been postponed - I would like to revive it. A quick recap (quotes edited for brevity):
On 23 January 2018 at 03:33, George Leslie-Waksman <waksman@gmail.com mailto:waksman@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: 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
Guido van Rossum wrote:
We can reconsider for 3.8. --Guido van Rossum (python.org/~guido)
What do you think about implementing this now? What should be the next step? Would it require a PEP?
On Mon, Feb 10, 2020 at 9:30 AM <b.marcinkowski@leomail.pl> wrote:
It's been two years since this proposal has been postponed - I would like to revive it.
...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
Guido van Rossum wrote:
We can reconsider for 3.8. --Guido van Rossum (python.org/~guido)
What do you think about implementing this now? What should be the next step? Would it require a PEP?
Just lending support to the idea: this would be a fantastic addition to the dataclasses module and I for one would use it CONSTANTLY. The style of coding I have become accustomed to relying upon (in the context of professional engineering calculations and analyses) has tended to favor kwarg-only arguments a majority of the time, for a variety of reasons. It's possible I'm creating problems for future me, but I don't think so. When using a good IDE or Jupyter, kwarg-only functions are far from a hassle to use, they are really a pleasure to use (and read).
This has been implemented already in attrs https://www.attrs.org/en/stable/examples.html#keyword-only-attributes so I think it makes sense to just translate this to dataclasses, or treat it as a starting point for a discussion if there are any objections to 1-to-1 translation from attrs.
I've had tens of people show interest in my proposal, asking what the blocker is in acceptance. As far as I know, no Python developer has shown any interest in it. My proposal is to automatically make any non-default fields become keyword-only `__init__` parameters, with no need to have any arguments passed to the `dataclass` decorator. My PR: https://github.com/python/cpython/pull/17322 My previous mailing-list message: https://mail.python.org/archives/list/python-ideas@python.org/message/2V2SCU...
I've seen comments from Eric V Smith in this thread and in the PR. If he's in favor and it can be done in a backwards compatible way then he can guide you to acceptance and merging of your PR. But like all of us he has limited time. On Wed, Mar 10, 2021 at 6:15 AM Laurie O <laurie_opperman@hotmail.com> wrote:
I've had tens of people show interest in my proposal, asking what the blocker is in acceptance. As far as I know, no Python developer has shown any interest in it.
My proposal is to automatically make any non-default fields become keyword-only `__init__` parameters, with no need to have any arguments passed to the `dataclass` decorator.
My PR: https://github.com/python/cpython/pull/17322
My previous mailing-list message: https://mail.python.org/archives/list/python-ideas@python.org/message/2V2SCU... _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KPVB2G... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
As I've said before, I'm not opposed to the feature of keyword-only arguments. I think it would be a great addition. However, the proposal from Laurie O is only for changing fields without default values following fields with default values to be keyword-only. At least that's how I understand it. So, that means that: @dataclasses.dataclass class Point: x: int = 0 y: int z: int t: int = 0 Would generate a __init__ with this signature: def __init__(self, x=0, *, y, z, t=0): While it's an interesting application, I think that's just too limiting. Among other things, I can't define a dataclass where all fields are keyword-only, or a class where there's only a single field and it's keyword-only. I also have to have at least one keyword-only field (here, y) that has no default value. z and t can have defaults, but not y. What I'd like to see is some way of having keyword-only arguments, with or without defaults. And I'd also like to see if we could get support for positional-only arguments at the same time. I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like: @dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0 And the dataclasses machinery would ignore the "_" field except for making everything after it keyword-only. Here the name "_" isn't special: any field (of any name) that's of type dataclasses.KEYWORD_ONLY would be ignored except for the keyword-only changing behavior. In some ways, it's like dataclasses.ClassVar, where the type is treated specially and the field doesn't become a __init__ argument. There are also issues with inheritance that would need to be thought through. This idea could also be extended for positional-only. I'm open to other suggestions. Eric On 3/10/2021 10:22 AM, Guido van Rossum wrote:
I've seen comments from Eric V Smith in this thread and in the PR. If he's in favor and it can be done in a backwards compatible way then he can guide you to acceptance and merging of your PR. But like all of us he has limited time.
On Wed, Mar 10, 2021 at 6:15 AM Laurie O <laurie_opperman@hotmail.com <mailto:laurie_opperman@hotmail.com>> wrote:
I've had tens of people show interest in my proposal, asking what the blocker is in acceptance. As far as I know, no Python developer has shown any interest in it.
My proposal is to automatically make any non-default fields become keyword-only `__init__` parameters, with no need to have any arguments passed to the `dataclass` decorator.
My PR: https://github.com/python/cpython/pull/17322 <https://github.com/python/cpython/pull/17322>
My previous mailing-list message: https://mail.python.org/archives/list/python-ideas@python.org/message/2V2SCU... <https://mail.python.org/archives/list/python-ideas@python.org/message/2V2SCUFMK7PK3DVA7D77AVXWIXNTSQDK/> _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KPVB2G... <https://mail.python.org/archives/list/python-ideas@python.org/message/KPVB2G64BHFGGOTXZQXT3AU7SIO5ABJE/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
-- --Guido van Rossum (python.org/~guido <http://python.org/~guido>) /Pronouns: he/him //(why is my pronoun here?)/ <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IN3D7U... Code of Conduct: http://python.org/psf/codeofconduct/
-- Eric V. Smith
You're right, the goal of my PR is not to allow specifying keyword-only (or positional-only) `__init__` parameters, but rather the goal is to allow child classes to be defined with non-defaulted fields (with defaulted fields in parent classes). Keyword-only parameters is simply an implementation detail. I don't know about positional-only parameters, but a more fleshed-out solution would do something similar to `attrs` (as linked above) where the fields can be specified as keyword-only, which then allows non-defaulted fields in child classes as a side-effect.
In my experience, a dataclass with more than a few attributes makes using positional arguments unwieldy and error-prone. I would think something like @dataclass(kwonly=bool) with default of False would be reasonably clean to implement and understand. While I appreciate supporting existing behavior for backward compatibility, I'm not so clear on the value of supporting a hybrid of positional and keyword __init__ arguments. Could you shed some light on your reasoning for supporting it? On Thu, 2021-03-11 at 00:47 -0500, Eric V. Smith wrote:
As I've said before, I'm not opposed to the feature of keyword-only arguments. I think it would be a great addition. However, the proposal from Laurie O is only for changing fields without default values following fields with default values to be keyword-only. At least that's how I understand it. So, that means that: @dataclasses.dataclass class Point: x: int = 0 y: int z: int t: int = 0 Would generate a __init__ with this signature: def __init__(self, x=0, *, y, z, t=0): While it's an interesting application, I think that's just too limiting. Among other things, I can't define a dataclass where all fields are keyword-only, or a class where there's only a single field and it's keyword-only. I also have to have at least one keyword-only field (here, y) that has no default value. z and t can have defaults, but not y. What I'd like to see is some way of having keyword-only arguments, with or without defaults. And I'd also like to see if we could get support for positional-only arguments at the same time. I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like: @dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0 And the dataclasses machinery would ignore the "_" field except for making everything after it keyword-only. Here the name "_" isn't special: any field (of any name) that's of type dataclasses.KEYWORD_ONLY would be ignored except for the keyword-only changing behavior. In some ways, it's like dataclasses.ClassVar, where the type is treated specially and the field doesn't become a __init__ argument. There are also issues with inheritance that would need to be thought through. This idea could also be extended for positional-only. I'm open to other suggestions. Eric On 3/10/2021 10:22 AM, Guido van Rossum wrote:
_______________________________________________ Python-ideas mailing list -- To unsubscribe send an email to
Message archived at Code of Conduct: -- Eric V. Smith
Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XSAYT2... Code of Conduct: http://python.org/psf/codeofconduct/
In my experience, a dataclass with more than a few attributes makes using positional arguments unwieldy and error-prone. Agreed, just like any function or class. I would think something like @dataclass(kwonly=/bool/) with default of False would be reasonably clean to implement and understand. Yes, I think that's a reasonable thing to do. But I don't want it to be
On 3/11/2021 1:41 AM, Paul Bryan wrote: the only option, I'd like to be able to mix and match some "normal" arguments and some keyword-only (and some positional-only).
While I appreciate supporting existing behavior for backward compatibility, I'm not so clear on the value of supporting a hybrid of positional and keyword __init__ arguments. Could you shed some light on your reasoning for supporting it?
The same as any function or class. From PEP 3102: def compare(a, b, *, key=None): This seems like a reasonable thing to want a dataclass to represent. Using my off-the-cuff proposal from below: @dataclasses.dataclass class Comparator: a: Any b: Any _: dataclasses.KEYWORD_ONLY key: Optional[Callable[whatever]] = None I don't want to restrict dataclasses: I'd like the full range of argument types to be available. This is especially true as dataclasses are used for more and more things (at least that's what happens in my code). Eric
On Thu, 2021-03-11 at 00:47 -0500, Eric V. Smith wrote:
As I've said before, I'm not opposed to the feature of keyword-only arguments. I think it would be a great addition.
However, the proposal from Laurie O is only for changing fields without default values following fields with default values to be keyword-only. At least that's how I understand it.
So, that means that:
@dataclasses.dataclass class Point: x: int = 0 y: int z: int t: int = 0
Would generate a __init__ with this signature:
def __init__(self, x=0, *, y, z, t=0):
While it's an interesting application, I think that's just too limiting. Among other things, I can't define a dataclass where all fields are keyword-only, or a class where there's only a single field and it's keyword-only. I also have to have at least one keyword-only field (here, y) that has no default value. z and t can have defaults, but not y.
What I'd like to see is some way of having keyword-only arguments, with or without defaults. And I'd also like to see if we could get support for positional-only arguments at the same time.
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
And the dataclasses machinery would ignore the "_" field except for making everything after it keyword-only. Here the name "_" isn't special: any field (of any name) that's of type dataclasses.KEYWORD_ONLY would be ignored except for the keyword-only changing behavior. In some ways, it's like dataclasses.ClassVar, where the type is treated specially and the field doesn't become a __init__ argument.
There are also issues with inheritance that would need to be thought through. This idea could also be extended for positional-only.
I'm open to other suggestions.
Eric
On 3/10/2021 10:22 AM, Guido van Rossum wrote:
_______________________________________________ Python-ideas mailing list -- To unsubscribe send an email to
Message archived at Code of Conduct: -- Eric V. Smith
Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XSAYT2... <https://mail.python.org/archives/list/python-ideas@python.org/message/XSAYT2MFOIBYHYINDHLPR7V2WHCWRYPE/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UGNLUW... Code of Conduct: http://python.org/psf/codeofconduct/
-- Eric V. Smith
In response to Eric V. Smith, if something like what you're suggesting were to be implemented I would much rather it be done with context managers than special values, because otherwise you once again end up in a situation where it's impossible to easily subclass a dataclass (which was one of the primary reasons this conversation even got started in the first place). So, for example: import dataclasses @dataclasses.dataclass class SomeClass: c: bool = False # a normal field with a default value does not # prevent subsequent positional fields from # having no default value (such as 'a' below) # however, all further normal fields now must # specify a default value (such as 'd' below) with dataclasses.positional(): a: int b: float = 3.14 # once a positional field with a default value shows up # all further positional fields and ALL normal fields # (even retroactively!) must also specify defaults # (for example, field 'c' above is # now forced to specify a default value) with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set) # once a keyword field with a default value shows up # all further keyword fields must also specify defaults d: dict = dataclasses.field(default_factory=dict) # this will generate an __init__ like: def __init__(self, a: int, b: float = 3.14, /, c: bool = False, d: dict = None, *, e: list, f: set = None): self.a = a self.b = b self.c = c self.d = dict() if d is None else d self.e = e self.f = set() if f is None else f # parameters are arranged in order as # positional -> normal -> keyword # within the order they were defined in each # individual category, but not necessarily # whatever order they were defined in overall # # This is subclass-friendly! # # it should hopefully be obvious that we could # have cut this class in half at literally any # point (as long as the the parent class has # the earlier arguments within each category) # and put the rest into a child class and # it would still have worked and generated the # same __init__ signature # # For example: @dataclasses.dataclass class Parent: c: bool = False with dataclasses.keyword(): e: list with dataclasses.positional(): a: int @dataclasses.dataclass class Child(Parent): with dataclasses.keyword(): f: set = dataclasses.field(default_factory=set) d: dict = dataclasses.field(default_factory=dict) with dataclasses.positional(): b: float = 3.14 # we have shuffled around the ordering of the # context managers and normal fields in both # classes and it STILL works unambiguously! Honestly, the more I think about it the more I'm +1 on something like this (even if it's not *exactly* my suggestion). Right now dataclasses do not support the full range of __init__ signatures you could generate with a normal class (and are extremely hostile to subclassing), and that is a failing that often forces people to fall back to normal classes in otherwise ideal dataclass use-case situations. On Thu, Mar 11, 2021 at 7:35 AM Eric V. Smith <eric@trueblade.com> wrote:
On 3/11/2021 1:41 AM, Paul Bryan wrote:
In my experience, a dataclass with more than a few attributes makes using positional arguments unwieldy and error-prone.
Agreed, just like any function or class.
I would think something like @dataclass(kwonly=*bool*) with default of False would be reasonably clean to implement and understand.
Yes, I think that's a reasonable thing to do. But I don't want it to be the only option, I'd like to be able to mix and match some "normal" arguments and some keyword-only (and some positional-only).
While I appreciate supporting existing behavior for backward compatibility, I'm not so clear on the value of supporting a hybrid of positional and keyword __init__ arguments. Could you shed some light on your reasoning for supporting it?
The same as any function or class. From PEP 3102:
def compare(a, b, *, key=None):
This seems like a reasonable thing to want a dataclass to represent. Using my off-the-cuff proposal from below:
@dataclasses.dataclass class Comparator: a: Any b: Any _: dataclasses.KEYWORD_ONLY key: Optional[Callable[whatever]] = None
I don't want to restrict dataclasses: I'd like the full range of argument types to be available. This is especially true as dataclasses are used for more and more things (at least that's what happens in my code).
Eric
On Thu, 2021-03-11 at 00:47 -0500, Eric V. Smith wrote:
As I've said before, I'm not opposed to the feature of keyword-only arguments. I think it would be a great addition.
However, the proposal from Laurie O is only for changing fields without default values following fields with default values to be keyword-only. At least that's how I understand it.
So, that means that:
@dataclasses.dataclass class Point: x: int = 0 y: int z: int t: int = 0
Would generate a __init__ with this signature:
def __init__(self, x=0, *, y, z, t=0):
While it's an interesting application, I think that's just too limiting. Among other things, I can't define a dataclass where all fields are keyword-only, or a class where there's only a single field and it's keyword-only. I also have to have at least one keyword-only field (here, y) that has no default value. z and t can have defaults, but not y.
What I'd like to see is some way of having keyword-only arguments, with or without defaults. And I'd also like to see if we could get support for positional-only arguments at the same time.
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
And the dataclasses machinery would ignore the "_" field except for making everything after it keyword-only. Here the name "_" isn't special: any field (of any name) that's of type dataclasses.KEYWORD_ONLY would be ignored except for the keyword-only changing behavior. In some ways, it's like dataclasses.ClassVar, where the type is treated specially and the field doesn't become a __init__ argument.
There are also issues with inheritance that would need to be thought through. This idea could also be extended for positional-only.
I'm open to other suggestions.
Eric On 3/10/2021 10:22 AM, Guido van Rossum wrote:
_______________________________________________ Python-ideas mailing list -- To unsubscribe send an email to
Message archived at Code of Conduct:
--
Eric V. Smith
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XSAYT2... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.orghttps://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UGNLUW... Code of Conduct: http://python.org/psf/codeofconduct/
-- Eric V. Smith
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6E6AI6... Code of Conduct: http://python.org/psf/codeofconduct/
This might be a bit of an extreme step to take, but has new syntax for this ever been discussed? Here I am using the same indicator for keyword arguments as in a function signature, hanging on a line by itself: @dataclasses.dataclass class Comparator: a: Any b: Any * key: Optional[Callable[whatever]] = None Could also support the positional only version: @dataclasses.dataclass class Comparator: / # args become positional after this line a: Any b: Any * # args become kwd args after this line key: Optional[Callable[whatever]] = None As far as how these standalone symbols would be parsed, I have no ideas on that. Because of the way Dataclasses work, the / and * lines would have to produce SOMETHING for the decorator to find. I'm just not sure what that something would be. But I think it looks pretty good if you're already familiar with python function signature syntax. Taking this to the logical conclusion: @dataclasses.dataclass class Comparator: / a: Any b: Any *others: Any # collect more positional args key: Optional[Callable[whatever]] = None **kwargs: Any # collect more kwd args On Thu, Mar 11, 2021, 12:51 AM Eric V. Smith <eric@trueblade.com> wrote:
As I've said before, I'm not opposed to the feature of keyword-only arguments. I think it would be a great addition.
However, the proposal from Laurie O is only for changing fields without default values following fields with default values to be keyword-only. At least that's how I understand it.
So, that means that:
@dataclasses.dataclass class Point: x: int = 0 y: int z: int t: int = 0
Would generate a __init__ with this signature:
def __init__(self, x=0, *, y, z, t=0):
While it's an interesting application, I think that's just too limiting. Among other things, I can't define a dataclass where all fields are keyword-only, or a class where there's only a single field and it's keyword-only. I also have to have at least one keyword-only field (here, y) that has no default value. z and t can have defaults, but not y.
What I'd like to see is some way of having keyword-only arguments, with or without defaults. And I'd also like to see if we could get support for positional-only arguments at the same time.
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
And the dataclasses machinery would ignore the "_" field except for making everything after it keyword-only. Here the name "_" isn't special: any field (of any name) that's of type dataclasses.KEYWORD_ONLY would be ignored except for the keyword-only changing behavior. In some ways, it's like dataclasses.ClassVar, where the type is treated specially and the field doesn't become a __init__ argument.
There are also issues with inheritance that would need to be thought through. This idea could also be extended for positional-only.
I'm open to other suggestions.
Eric On 3/10/2021 10:22 AM, Guido van Rossum wrote:
I've seen comments from Eric V Smith in this thread and in the PR. If he's in favor and it can be done in a backwards compatible way then he can guide you to acceptance and merging of your PR. But like all of us he has limited time.
On Wed, Mar 10, 2021 at 6:15 AM Laurie O <laurie_opperman@hotmail.com> wrote:
I've had tens of people show interest in my proposal, asking what the blocker is in acceptance. As far as I know, no Python developer has shown any interest in it.
My proposal is to automatically make any non-default fields become keyword-only `__init__` parameters, with no need to have any arguments passed to the `dataclass` decorator.
My PR: https://github.com/python/cpython/pull/17322
My previous mailing-list message: https://mail.python.org/archives/list/python-ideas@python.org/message/2V2SCU... _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KPVB2G... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.orghttps://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IN3D7U... Code of Conduct: http://python.org/psf/codeofconduct/
-- Eric V. Smith
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XSAYT2... Code of Conduct: http://python.org/psf/codeofconduct/
On 3/11/21 4:20 AM, Ricky Teachey wrote:
This might be a bit of an extreme step to take, but has new syntax for this ever been discussed? Here I am using the same indicator for keyword arguments as in a function signature, hanging on a line by itself:
@dataclasses.dataclass class Comparator: a: Any b: Any * key: Optional[Callable[whatever]] = None
Could also support the positional only version:
@dataclasses.dataclass class Comparator: / # args become positional after this line a: Any b: Any * # args become kwd args after this line key: Optional[Callable[whatever]] = None
Actually, the '/' indicates that names /before/ it are positional only. -- ~Ethan~
On Thu, Mar 11, 2021 at 10:00 AM Ethan Furman <ethan@stoneleaf.us> wrote:
On 3/11/21 4:20 AM, Ricky Teachey wrote:
This might be a bit of an extreme step to take, but has new syntax for this ever been discussed? Here I am using the same indicator for keyword arguments as in a function signature, hanging on a line by itself:
...
@dataclasses.dataclass class Comparator: / # args become positional after this line a: Any b: Any * # args become kwd args after this line key: Optional[Callable[whatever]] = None
Actually, the '/' indicates that names /before/ it are positional only.
-- ~Ethan~
Yes you're of course correct! Sorry! <http://python.org/psf/codeofconduct/> So the corrected version would be: @dataclasses.dataclass class Comparator: a: Any b: Any / # args before this line positional only * # args become kwd args after this line key: Optional[Callable[whatever]] = None --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
Given that @dataclass is a decorator, and otherwise you're just defining a class, some concerns: 1. Such proposed syntax would require a change to the language specification. 2. It would seem that / and * in a class not decorated with @dataclass would be of no other utility. Paul On Thu, 2021-03-11 at 10:08 -0500, Ricky Teachey wrote:
On Thu, Mar 11, 2021 at 10:00 AM Ethan Furman <ethan@stoneleaf.us> wrote:
On 3/11/21 4:20 AM, Ricky Teachey wrote:
This might be a bit of an extreme step to take, but has new syntax for this ever been discussed? Here I am using the same indicator for keyword arguments as in a function signature, hanging on a line by itself:
...
@dataclasses.dataclass class Comparator: / # args become positional after this line a: Any b: Any * # args become kwd args after this line key: Optional[Callable[whatever]] = None
Actually, the '/' indicates that names /before/ it are positional only.
-- ~Ethan~
Yes you're of course correct! Sorry!
So the corrected version would be:
@dataclasses.dataclass class Comparator: a: Any b: Any / # args before this line positional only * # args become kwd args after this line key: Optional[Callable[whatever]] = None
--- Ricky.
"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/L6Y5C7... Code of Conduct: http://python.org/psf/codeofconduct/
On Thu, Mar 11, 2021 at 10:20 AM Paul Bryan <pbryan@anode.ca> wrote:
Given that @dataclass is a decorator, and otherwise you're just defining a class, some concerns:
1. Such proposed syntax would require a change to the language specification.
2. It would seem that / and * in a class not decorated with @dataclass would be of no other utility.
Paul
Well the utility of this, without dataclasses, or only for the type checker as far as I know: class C: member: Any Perhaps the additional syntax could also be useful for the type checker? --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
On 3/11/2021 10:23 AM, Ricky Teachey wrote:
On Thu, Mar 11, 2021 at 10:20 AM Paul Bryan <pbryan@anode.ca <mailto:pbryan@anode.ca>> wrote:
Given that @dataclass is a decorator, and otherwise you're just defining a class, some concerns:
1. Such proposed syntax would require a change to the language specification.
2. It would seem that / and * in a class not decorated with @dataclass would be of no other utility.
Paul
Well the utility of this, without dataclasses, or only for the type checker as far as I know:
class C: member: Any
Perhaps the additional syntax could also be useful for the type checker?
Type checkers certainly have to be aware of the generated __init__, so they're definitely involved. As long as we're going all "pie in the sky", I'd like to see dataclasses baked in to the interpreter. Something like: dataclass Point: x: int y: int That's actually a typo I make all the time! But I don't think there's really any chance of doing anything requiring changes to the interpreter. So we're back to either using field() or my marker types. Any other ideas that could be implemented today? I don't think the "with" suggestion could be made to work, but maybe I lack imagination. Eric
On Thu, Mar 11, 2021 at 11:59 AM Eric V. Smith <eric@trueblade.com> wrote:
On 3/11/2021 10:23 AM, Ricky Teachey wrote:
On Thu, Mar 11, 2021 at 10:20 AM Paul Bryan <pbryan@anode.ca> wrote:
Given that @dataclass is a decorator, and otherwise you're just defining a class, some concerns:
1. Such proposed syntax would require a change to the language specification.
2. It would seem that / and * in a class not decorated with @dataclass would be of no other utility.
Paul
Well the utility of this, without dataclasses, or only for the type checker as far as I know:
class C: member: Any
Perhaps the additional syntax could also be useful for the type checker?
Type checkers certainly have to be aware of the generated __init__, so they're definitely involved.
As long as we're going all "pie in the sky", I'd like to see dataclasses baked in to the interpreter. Something like:
dataclass Point: x: int y: int
That's actually a typo I make all the time!
Yes, please! But I don't think there's really any chance of doing anything requiring
changes to the interpreter. So we're back to either using field() or my marker types. Any other ideas that could be implemented today? I don't think the "with" suggestion could be made to work, but maybe I lack imagination.
Eric
Not with that attitude! ;) <http://python.org/psf/codeofconduct/> --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this? class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list >>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, } The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-) -- ~Ethan~
It's current convention (and is used by typing module and static type checkers) that string annotations evaluate to valid Python types. On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-)
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/BIAVX4... Code of Conduct: http://python.org/psf/codeofconduct/
On 3/11/21 10:50 AM, Paul Bryan wrote:
On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-)
It's current convention (and is used by typing module and static type checkers) that string annotations evaluate to valid Python types.
So make '/' and '*' be imports from dataclasses: from dataclasses import dataclass, PosOnly, KWOnly -- ~Ethan~
If you're proposing something like this, then I think it would be compatible: class Hmm: # this: int that: float # pos: PosOnly # these: str those: str # key: KWOnly # some: list On Thu, 2021-03-11 at 14:06 -0800, Ethan Furman wrote:
On 3/11/21 10:50 AM, Paul Bryan wrote:
On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-)
It's current convention (and is used by typing module and static type checkers) that string annotations evaluate to valid Python types.
So make '/' and '*' be imports from dataclasses:
from dataclasses import dataclass, PosOnly, KWOnly
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6L4W5O... Code of Conduct: http://python.org/psf/codeofconduct/
Disclaimer: I posted this earlier today but I think due to some first-post moderation related issues (that I've hopefully now gotten sorted out!) it may not have gone through. I'm posting this again just in case. If it's gone through and you've already seen it then I'm super sorry, please just ignore this. If something like what you're suggesting were to be implemented I would much rather it be done with context managers than position-dependent special values, because otherwise you once again end up in a situation where it's impossible to easily subclass a dataclass (which was one of the primary reasons this conversation even got started in the first place). So, for example: import dataclasses @dataclasses.dataclass class SomeClass: c: bool = False # a normal field with a default value does not # prevent subsequent positional fields from # having no default value (such as 'a' below) # however, all further normal fields now must # specify a default value (such as 'd' below) with dataclasses.positional(): a: int b: float = 3.14 # once a positional field with a default value shows up # all further positional fields and ALL normal fields # (even retroactively!) must also specify defaults # (for example, field 'c' above is # now forced to specify a default value) with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set) # once a keyword field with a default value shows up # all further keyword fields must also specify defaults d: dict = dataclasses.field(default_factory=dict) # This ordering is clearly insane, but the essential # point is that it works even with weird ordering # which is necessary for it to work when subclassing # where the order will almost always be wonky # # A sane version of the above would be: @dataclasses.dataclass class SomeClass: with dataclasses.positional(): a: int b: float = 3.14 c: bool = False d: dict = dataclasses.field(default_factory=dict) with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set) # either of the above will generate an __init__ like: def __init__(self, a: int, b: float = 3.14, /, c: bool = False, d: dict = None, *, e: list, f: set = None): self.a = a self.b = b self.c = c self.d = dict() if d is None else d self.e = e self.f = set() if f is None else f # parameters are arranged in order as # positional -> normal -> keyword # within the order they were defined in each # individual category, but not necessarily # whatever order they were defined in overall # # This is subclass-friendly! # # it should hopefully be obvious that we could # have cut this class in half at literally any # point (as long as the the parent class has # the earlier arguments within each category) # and put the rest into a child class and # it would still have worked and generated the # same __init__ signature # # For example: @dataclasses.dataclass class Parent: with dataclasses.positional(): a: int c: bool = False with dataclasses.keyword(): e: list @dataclasses.dataclass class Child(Parent): with dataclasses.positional(): b: float = 3.14 d: dict = dataclasses.field(default_factory=dict) with dataclasses.keyword(): f: set = dataclasses.field(default_factory=set) # Child now has the same __init__ signature as # SomeClass above (In case the above code doesn't render properly on your screen, I've uploaded it to GitHub at: https://github.com/matthewgdv/dataclass_arg_contextmanager/blob/main/example... ) Honestly, the more I think about it, the more I love the idea of something like this (even if it's not *exactly* the same as my suggestion). Right now dataclasses do not support the full range of __init__ signatures you could generate with a normal class, and they are extremely hostile to subclassing. That is a failing that often forces people to fall back to normal classes in otherwise ideal dataclass use-case situations. On Thu, Mar 11, 2021 at 10:15 PM Paul Bryan <pbryan@anode.ca> wrote:
If you're proposing something like this, then I think it would be compatible:
class Hmm:
#
this: int
that: float
#
pos: PosOnly
#
these: str
those: str
#
key: KWOnly
#
some: list
On Thu, 2021-03-11 at 14:06 -0800, Ethan Furman wrote:
On 3/11/21 10:50 AM, Paul Bryan wrote:
On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-)
It's current convention (and is used by typing module and static type checkers) that string annotations evaluate to valid Python types.
So make '/' and '*' be imports from dataclasses:
from dataclasses import dataclass, PosOnly, KWOnly
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6L4W5O... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/VPSE34... Code of Conduct: http://python.org/psf/codeofconduct/
The syntax of what you're proposing seems fairly intuitive (they might not even need to be callable). I'm struggling to envision how the context manager would acquire scope to mark fields up in the (not yet defined) dataclass, and to capture the attributes that are being defined within. On Thu, 2021-03-11 at 22:53 +0000, Matt del Valle wrote: Disclaimer: I posted this earlier today but I think due to some first- post moderation related issues (that I've hopefully now gotten sorted o ut!) it may not have gone through. I'm posting this again just in case. If it's gone through and you've already seen it then I'm super sorry, please just ignore this. If something like what you're suggesting were to be implemented I would much rather it be done with context managers than position- dependent special values, because otherwise you once again end up in a situation where it's impossible to easily subclass a dataclass (which w as one of the primary reasons this conversation even got started in the first place). So, for example: import dataclasses @dataclasses.dataclass class SomeClass: c: bool = False # a normal field with a default value does not # prevent subsequent positional fields from # having no default value (such as 'a' below) # however, all further normal fields now must # specify a default value (such as 'd' below) with dataclasses.positional(): a: int b: float = 3.14 # once a positional field with a default value shows up # all further positional fields and ALL normal fields # (even retroactively!) must also specify defaults # (for example, field 'c' above is # now forced to specify a default value) with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set) # once a keyword field with a default value shows up # all further keyword fields must also specify defaults d: dict = dataclasses.field(default_factory=dict) # This ordering is clearly insane, but the essential # point is that it works even with weird ordering # which is necessary for it to work when subclassing # where the order will almost always be wonky # # A sane version of the above would be: @dataclasses.dataclass class SomeClass: with dataclasses.positional(): a: int b: float = 3.14 c: bool = False d: dict = dataclasses.field(default_factory=dict) with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set) # either of the above will generate an __init__ like: def __init__(self, a: int, b: float = 3.14, /, c: bool = False, d: dict = None, *, e: list, f: set = None): self.a = a self.b = b self.c = c self.d = dict() if d is None else d self.e = e self.f = set() if f is None else f # parameters are arranged in order as # positional -> normal -> keyword # within the order they were defined in each # individual category, but not necessarily # whatever order they were defined in overall # # This is subclass-friendly! # # it should hopefully be obvious that we could # have cut this class in half at literally any # point (as long as the the parent class has # the earlier arguments within each category) # and put the rest into a child class and # it would still have worked and generated the # same __init__ signature # # For example: @dataclasses.dataclass class Parent: with dataclasses.positional(): a: int c: bool = False with dataclasses.keyword(): e: list @dataclasses.dataclass class Child(Parent): with dataclasses.positional(): b: float = 3.14 d: dict = dataclasses.field(default_factory=dict) with dataclasses.keyword(): f: set = dataclasses.field(default_factory=set) # Child now has the same __init__ signature as # SomeClass above (In case the above code doesn't render properly on your screen, I've up loaded it to GitHub at: https://github.com/matthewgdv/dataclass_arg_co ntextmanager/blob/main/example.py) Honestly, the more I think about it, the more I love the idea of someth ing like this (even if it's not exactly the same as my suggestion). Rig ht now dataclasses do not support the full range of __init__ signatures you could generate with a normal class, and they are extremely hostile to subclassing. That is a failing that often forces people to fall bac k to normal classes in otherwise ideal dataclass use-case situations. On Thu, Mar 11, 2021 at 10:15 PM Paul Bryan <pbryan@anode.ca> wrote:
If you're proposing something like this, then I think it would be com
patible:
class Hmm: # this: int that: float # pos: PosOnly # these: str those: str # key: KWOnly # some: list
On Thu, 2021-03-11 at 14:06 -0800, Ethan Furman wrote:
On 3/11/21 10:50 AM, Paul Bryan wrote:
On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to
field() doesn't sound awesome, but could be made to work. Or maybe sp
ecial field names or types? I'm not crazy about that, but using spe
cial types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual
name is irrelevant. They do have to be unique, though. ;-)
It's current convention (and is used by typing module and static
type checkers) that string annotations evaluate to valid Python types.
So make '/' and '*' be imports from dataclasses:
from dataclasses import dataclass, PosOnly, KWOnly
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-i deas@python.org/message/6L4W5OB23FBWZ7EZYDNCYSGT2CUAKYSX/ Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ide as@python.org/message/VPSE34Z35XOXGFJMGTMLWDAMF7JKJYOJ/ Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas @python.org/message/WBL4X46QG2HY5ZQWYVX4MXG5LK7QXBWB/ Code of Conduct: http://python.org/psf/codeofconduct/
Just off the top of my head, a context manager like this would give access to the required local scope, if inspecting the execution frames isn't considered too hacky. class LocalsProcessor: def __enter__(self): self.locals_ref = inspect.currentframe().f_back.f_locals self.locals_prev = copy.deepcopy(self.locals_ref) # deep copy to ensure we also get a copy of the # __annotations__ def __exit__(self, exception_type, exception_value, traceback): ... # modify self.locals_ref based on the difference between # self.locals_ref and self.locals_prev and their # respective __annotations__ An issue I can see with this approach is that the context manager can only work off the difference between locals() before and after its scope, so it would ignore a duplicate assignment to the same value as before, for a name that existed before the context manager entered, in a way that might be unexpected: class Example: a: str b: int with LocalsProcessor(): a: bool # we can detect that 'a' changed and what it changed to/from # because its value in the __annotations__ dict is different b: int # there is no way to detect that 'b' was redeclared within # the scope of the contextmanager because it has the same # annotation before and after Granted, I have no idea who would ever write code like this (!) but I thought I'd mention that as a problematic edge-case. Maybe there's a better way to approach this that I can't think of. Or maybe it's possible that using context managers for this isn't realistic because of implementation issues that just can't be resolved. I just really like the semantics of it :) On Thu, Mar 11, 2021 at 11:08 PM Paul Bryan <pbryan@anode.ca> wrote:
The syntax of what you're proposing seems fairly intuitive (they might not even need to be callable). I'm struggling to envision how the context manager would acquire scope to mark fields up in the (not yet defined) dataclass, and to capture the attributes that are being defined within.
On Thu, 2021-03-11 at 22:53 +0000, Matt del Valle wrote:
Disclaimer: I posted this earlier today but I think due to some first-post moderation related issues (that I've hopefully now gotten sorted out!) it may not have gone through. I'm posting this again just in case. If it's gone through and you've already seen it then I'm super sorry, please just ignore this.
If something like what you're suggesting were to be implemented I would much rather it be done with context managers than position-dependent special values, because otherwise you once again end up in a situation where it's impossible to easily subclass a dataclass (which was one of the primary reasons this conversation even got started in the first place). So, for example:
import dataclasses
@dataclasses.dataclass class SomeClass: c: bool = False # a normal field with a default value does not # prevent subsequent positional fields from # having no default value (such as 'a' below) # however, all further normal fields now must # specify a default value (such as 'd' below)
with dataclasses.positional(): a: int b: float = 3.14 # once a positional field with a default value shows up # all further positional fields and ALL normal fields # (even retroactively!) must also specify defaults # (for example, field 'c' above is # now forced to specify a default value)
with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set) # once a keyword field with a default value shows up # all further keyword fields must also specify defaults
d: dict = dataclasses.field(default_factory=dict) # This ordering is clearly insane, but the essential # point is that it works even with weird ordering # which is necessary for it to work when subclassing # where the order will almost always be wonky # # A sane version of the above would be:
@dataclasses.dataclass class SomeClass: with dataclasses.positional(): a: int b: float = 3.14
c: bool = False d: dict = dataclasses.field(default_factory=dict)
with dataclasses.keyword(): e: list f: set = dataclasses.field(default_factory=set)
# either of the above will generate an __init__ like: def __init__(self, a: int, b: float = 3.14, /, c: bool = False, d: dict = None, *, e: list, f: set = None): self.a = a self.b = b self.c = c self.d = dict() if d is None else d self.e = e self.f = set() if f is None else f # parameters are arranged in order as # positional -> normal -> keyword # within the order they were defined in each # individual category, but not necessarily # whatever order they were defined in overall # # This is subclass-friendly! # # it should hopefully be obvious that we could # have cut this class in half at literally any # point (as long as the the parent class has # the earlier arguments within each category) # and put the rest into a child class and # it would still have worked and generated the # same __init__ signature # # For example:
@dataclasses.dataclass class Parent: with dataclasses.positional(): a: int
c: bool = False
with dataclasses.keyword(): e: list
@dataclasses.dataclass class Child(Parent): with dataclasses.positional(): b: float = 3.14
d: dict = dataclasses.field(default_factory=dict)
with dataclasses.keyword(): f: set = dataclasses.field(default_factory=set) # Child now has the same __init__ signature as # SomeClass above
(In case the above code doesn't render properly on your screen, I've uploaded it to GitHub at: https://github.com/matthewgdv/dataclass_arg_contextmanager/blob/main/example... )
Honestly, the more I think about it, the more I love the idea of something like this (even if it's not *exactly* the same as my suggestion). Right now dataclasses do not support the full range of __init__ signatures you could generate with a normal class, and they are extremely hostile to subclassing. That is a failing that often forces people to fall back to normal classes in otherwise ideal dataclass use-case situations.
On Thu, Mar 11, 2021 at 10:15 PM Paul Bryan <pbryan@anode.ca> wrote:
If you're proposing something like this, then I think it would be compatible:
class Hmm:
#
this: int
that: float
#
pos: PosOnly
#
these: str
those: str
#
key: KWOnly
#
some: list
On Thu, 2021-03-11 at 14:06 -0800, Ethan Furman wrote:
On 3/11/21 10:50 AM, Paul Bryan wrote:
On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-)
It's current convention (and is used by typing module and static type checkers) that string annotations evaluate to valid Python types.
So make '/' and '*' be imports from dataclasses:
from dataclasses import dataclass, PosOnly, KWOnly
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6L4W5O... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/VPSE34... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WBL4X4... Code of Conduct: http://python.org/psf/codeofconduct/
That's basically my suggestion with dataclasses.KEYWORD_ONLY, and obviously POSITIONAL_ONLY also needs to exist. I'd be flexible on how to spell these. I'm currently thinking we need some way to switch back to "normal" (non-keyword-only, non-positional-only) fields. But I'm not done with my prototype or use cases. I also think @dataclass(kwonly=True) is a case that should be supported, since I think the most common usage will be to make every field keyword-only. I haven't yet looked at what attrs does. Eric On 3/11/2021 5:12 PM, Paul Bryan wrote:
If you're proposing something like this, then I think it would be compatible:
class Hmm: # this: int that: float # pos: PosOnly # these: str those: str # key: KWOnly # some: list
On Thu, 2021-03-11 at 14:06 -0800, Ethan Furman wrote:
On 3/11/21 10:50 AM, Paul Bryan wrote:
On Thu, 2021-03-11 at 10:45 -0800, Ethan Furman wrote:
On 3/10/21 9:47 PM, Eric V. Smith wrote:
I'm not sure of the best way to achieve this. Using flags to field() doesn't sound awesome, but could be made to work. Or maybe special field names or types? I'm not crazy about that, but using special types would let you do something like:
@dataclasses.dataclass class Point: x: int = 0 _: dataclasses.KEYWORD_ONLY y: int z: int t: int = 0
Maybe something like this?
class Hmm: # this: int that: float # pos: '/' # these: str those: str # key: '*' # some: list
>>> Hmm.__dict__['__annotations__'] { 'this': <class 'int'>, 'that': <class 'float'>, 'pos': '/', 'these': <class 'str'>, 'those': <class 'str'>, 'key': '*', 'some': <class 'list'>, }
The name of 'pos' and 'key' can be convention, since the actual name is irrelevant. They do have to be unique, though. ;-)
It's current convention (and is used by typing module and static type checkers) that string annotations evaluate to valid Python types.
So make '/' and '*' be imports from dataclasses:
from dataclasses import dataclass, PosOnly, KWOnly
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6L4W5O... <https://mail.python.org/archives/list/python-ideas@python.org/message/6L4W5OB23FBWZ7EZYDNCYSGT2CUAKYSX/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/VPSE34... Code of Conduct: http://python.org/psf/codeofconduct/
-- Eric V. Smith
participants (12)
-
b.marcinkowski@leomail.pl
-
Bartosz Marcinkowski
-
Eric V. Smith
-
Ethan Furman
-
George Leslie-Waksman
-
Guido van Rossum
-
Ivan Levkivskyi
-
Laurie O
-
Matt del Valle
-
Paul Bryan
-
Ricky Teachey
-
Sylvain MARIE