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.
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/
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)
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)
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)
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.orgmailto: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.orgmailto: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/~guidohttp://python.org/~guido)
-- --Guido van Rossum (python.org/~guidohttp://python.org/~guido)
-- --Guido van Rossum (python.org/~guidohttp://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.