ericvsmith / dataclasses

Apache License 2.0
587 stars 53 forks source link

Error if there are Default Fields in the Parent Class #112

Closed vansivallab closed 6 years ago

vansivallab commented 6 years ago

A TypeError is raised when trying to create a subclass off a class which contains a default field.

@dataclass
class Foo:
    some_default: dict = field(default_factory=dict)

@dataclass
class Bar(Foo):
    other_field: int

Error output:

      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
ericvsmith commented 6 years ago

This is the defined behavior. The generated Bar.__init__ would look like:

def __init__(self, some_default: dict = <something>, other_field: int)

Which is an error.

What would you suggest the behavior should be?

gwax commented 6 years ago

We could fall back to (or explicitly allow) keyword only arguments in this case such that we end up with a signature for Bar.__init__ of:

def __init__(self, *, some_default: dict = <something>, other_field: int):
    ...

which is not an error.

ericvsmith commented 6 years ago

I think that's more complexity than we want to promote.

gwax commented 6 years ago

Possibly as a decorator argument:

@dataclass(keyword_only=True)
class Bar(Foo):
    other_field: int
ericvsmith commented 6 years ago

That might be reasonable. Now that I've merged this to cpython, suggestions like this should probably go to the python-ideas mailing list.

ilevkivskyi commented 6 years ago

@gwax I think this adds unnecessary complexity. What is your use case? Also note that the current behaviour is consistent with typing.NamedTuple and mypy_extensions.TypedDict (which will likely be promoted to typing).

ericvsmith commented 6 years ago

@vansivallab: I'm closing this issue. Please bring it up on python-ideas if you're still interesting in it.

My goal is to shut down this repo except for backporting to Python 3.6.

riazrizvipn commented 5 years ago

https://stackoverflow.com/questions/51575931/class-inheritance-in-python-3-7-dataclasses