Skip to content

[Idea] Add typing.Abstract to allow hinting abstract attributes #106248

Description

@randolf-scholz

Previous discussion: https://discuss.python.org/t/abc-add-abstract-attributes-via-abstract-type-hint/26164

Feature or enhancement

Sometimes, we want to indicate that all subclasses of an abstract base class must have some attribute. Currently, this can only be done by combining @property and @abstractmethod:

from abc import ABC, abstractmethod

class Foo(ABC):
    @property
    @abstractmethod
    def myattr(self) -> int: ...
However, this is verbose. Even more so if we want to assert that the attribute must be settable.
from abc import ABC, abstractmethod

class Foo(ABC):
    @property
    @abstractmethod
    def myattr(self) -> int: ...

    @myattr.setter
    @abstractmethod
    def myattr(self, x: int, /) -> None: ...


class Bar(Foo):
    myattr: int = 1

Bar()

Secondly, since @classmethod and @property cannot be combined anymore, we cannot use this to assert the existence of abstract class-attributes.

Finally, a user might wish to indicate that an attribute has to be implemented by a property. (For instance, because the attribute is expensive to compute, so the lazy evaluation of properties might be important). If the new method of hinting abstract attributes is accepted, then at some point in the future the way ABCs work can be changed to require that @abstractmethod-properties must be implement as properties by the subclass.

Pitch

Add two new typing constructs: Abstract and Mutable OR AbstractAttribute and AbstractMutableAttribute, analogous to e.g. Sequence and MutableSequence. (Not sure which one is preferable)

  • AbstractAttribute/Abstract guarantees read-access. Subclasses can implement it via a @property or a regular attribute.
  • AbstractMutableAttribute/Abstract[Mutable] guarantees read and write access. Subclasses can implement via a @property with getter and setter or as a regular attribute.
  • Either can be combined with ClassVar to indicate abstract class-attributes. (⇝ composability with other 'modifiers' like e.g. dataclasses.InitVar ?!)

For example:

class Foo(ABC):
   x: Abstract[int]               # ALT: AbstractAttribute[int]
   y: Abstract[Mutable[float]]    # ALT: AbstractMutableAttribute[float]
   z: Abstract[ClassVar[int]]     # ALT: AbstractAttribute[ClassVar[int]]

class Bar(Foo):
    z: ClassVar[int] = 42

    @property
    def x(self) -> int:
        return 0  

    @property
    def y(self) -> int:
        return 1

Would fail since Bar is missing the setter for y.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions