Paul Pinterits <[email protected]> added the comment:
> dataclasses doesn't know the signature of the base class's __init__, so it
> can't know how to call it.
The dataclass doesn't need to know what arguments the parent __init__ accepts.
It should consume the arguments it needs to initialize its instance attributes,
and forward the rest to the parent __init__.
> The typical way to handle this is for the derived class to add a
> __post_init__() that calls into the base class's __init__().
How is that supposed to work? Like you said, the class doesn't know what
arguments its parent constructor requires. If the derived class doesn't
implement a custom __init__, @dataclass will generate one with an (probably)
incorrect signature. Changing the signature is pretty much the only reason why
you would need to implement a custom __init__, after all. For example:
```
@dataclasses.dataclass
class Foo:
foo: int
def __init__(self):
self.foo = 5
@dataclasses.dataclass
class Bar(Foo):
bar: int
def __post_init__(self):
super().__init__()
bar = Bar(1) # TypeError: __init__() missing 1 required positional argument:
'bar'
```
And even if this workaround actually worked, it would only be applicable if you
*know* you're dealing with a dataclass and you'll have this problem. If you're
writing something like a class decorator (similar to @dataclass), you don't
know if the decorated class will be a dataclass or not. If it's a regular
class, you can rely on the __init__s being chain-called. If it's a dataclass,
you can't. Therefore, a class decorator that generates/replaces the __init__
method would need to take special care to be compatible with dataclasses, just
because dataclasses don't follow basic OOP design principles.
Consider this trivial class decorator that generates an __init__ method:
```
def log_init(cls):
try:
original_init = vars(cls)['__init__']
except KeyError:
def original_init(self, *args, **kwargs):
super(cls, self).__init__(*args, **kwargs)
def __init__(self, *args, **kwargs):
print(f'{cls.__name__}.__init__ was called')
original_init(self, *args, **kwargs)
cls.__init__ = __init__
return cls
@log_init
@dataclasses.dataclass
class Foo:
foo: int
@dataclasses.dataclass
class Bar(Foo):
bar: int
Foo(1) # Prints "Foo.__init__ was called"
Bar(1, 2) # Prints nothing
```
How do you implement this in a way that is compatible with @dataclass?
----------
_______________________________________
Python tracker <[email protected]>
<https://bugs.python.org/issue43835>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com