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/