Inheritance from base class with property in class makes them non-instantiatable · Issue #91393 · python/cpython (original) (raw)
BPO | 47237 |
---|---|
Nosy | @gvanrossum, @ericvsmith, @JelleZijlstra, @Fidget-Spinner, @AlexWaygood, @Germandrummer92 |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = None closed_at = None created_at = <Date 2022-04-06.08:41:16.184> labels = ['type-bug', 'library', '3.10'] title = 'Inheritance from base class with property in class makes them non-instantiatable' updated_at = <Date 2022-04-06.15:54:34.248> user = 'https://github.com/Germandrummer92'
bugs.python.org fields:
activity = <Date 2022-04-06.15:54:34.248> actor = 'eric.smith' assignee = 'none' closed = False closed_date = None closer = None components = ['Library (Lib)'] creation = <Date 2022-04-06.08:41:16.184> creator = 'Germandrummer92' dependencies = [] files = [] hgrepos = [] issue_num = 47237 keywords = [] message_count = 5.0 messages = ['416846', '416853', '416880', '416881', '416882'] nosy_count = 6.0 nosy_names = ['gvanrossum', 'eric.smith', 'JelleZijlstra', 'kj', 'AlexWaygood', 'Germandrummer92'] pr_nums = [] priority = 'normal' resolution = None stage = None status = 'open' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue47237' versions = ['Python 3.10']
Hi,
According to https://peps.python.org/pep-0544/#explicitly-declaring-implementation it should be possible to explicitly inherit from Protocols. This however breaks the dataclass constructor when using the @Property decorator in the protocol, see this example:
from typing import Protocol from dataclasses import dataclass
class SomeProtocol(Protocol): @property def some_value(self) -> str: ...
@dataclass class SomeDataclasss(SomeProtocol): some_value: str
if name == 'main': a = SomeDataclasss(some_value="value") # this crashes with AttributeError: can't set attribute 'some_value'
The pattern of @Property in the protocol is one taken from the mypy docs (see https://mypy.readthedocs.io/en/stable/protocols.html#recursive-protocols for example).
When removing the explicit inheritiance mypy also correctly typechecks the dataclass implementation when doing something like, only the explicit inheritance seems to fail in python
a: SomeProtocol = SomeDataclass()
Here's the error without dataclasses:
------
from typing import Protocol
class SomeProtocol(Protocol): @property def some_value(self) -> str: ...
class SomeClass(SomeProtocol): def init(self, some_value): self.some_value = some_value
if name == 'main': a = SomeClass(some_value="value")
Traceback (most recent call last): File "foo.py", line 12, in a = SomeClass(some_value="value") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "foo.py", line 9, in init self.some_value = some_value ^^^^^^^^^^^^^^^ AttributeError: property 'some_value' of 'SomeClass' object has no setter
And here it is without Protocol:
--------
class SomeProperty: @property def some_value(self) -> str: ...
class SomeClass(SomeProperty): def init(self, some_value): self.some_value = some_value
if name == 'main': a = SomeClass(some_value="value")
Traceback (most recent call last): File "foo.py", line 10, in a = SomeClass(some_value="value") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "foo.py", line 7, in init self.some_value = some_value ^^^^^^^^^^^^^^^ AttributeError: property 'some_value' of 'SomeClass' object has no setter
ericvsmith changed the titleInheritance from Protocol with property in dataclass makes them non-instantiatable Inheritance from Protocol with property in class makes them non-instantiatable
ericvsmith changed the titleInheritance from Protocol with property in dataclass makes them non-instantiatable Inheritance from Protocol with property in class makes them non-instantiatable
ericvsmith changed the titleInheritance from Protocol with property in class makes them non-instantiatable Inheritance from base class with property in class makes them non-instantiatable
ericvsmith changed the titleInheritance from Protocol with property in class makes them non-instantiatable Inheritance from base class with property in class makes them non-instantiatable
So is the conclusion that this should be closed as "not a bug"?
I think the behavior with regular classes is expected (that's just how inheritance works), but a case could be made that dataclasses should handle this case specially.
What would dataclasses do that's different from a regular class?
With CPython 3.10.7 and mypy 0.982
import abc import dataclasses as dc from typing import Protocol
class Proto(Protocol): @property @abc.abstractmethod def prop(self) -> int: raise NotImplementedError
@dc.dataclass class Implicit: prop: int
@dc.dataclass class Explicit(Proto): prop: int
x: Proto = Implicit(42) y: Proto = Explicit(42)
The results are:
$ python test.py
Traceback (most recent call last):
File "/private/tmp/test.py", line 24, in <module>
y: Proto = Explicit(42)
TypeError: Can't instantiate abstract class Explicit with abstract method prop
$ mypy test.py
Success: no issues found in 1 source file
I see this as a perhaps surprising result of how all these things work, not as a bug. Note that Protocol
isn't needed, the same error is given if Proto
doesn't inherit from Protocol
. A workaround would be to set a default value in the Explicit
subclass (prop: int = 0
). But mostly my view is "don't do that".
Another workaround that removes inheritance but preserves explicit type checking at the place of definition:
if TYPE_CHECKING: _: type[Proto] = Explicit
@dc.dataclass class Explicit: prop: int