Я последовал этому ответу для Python 3.3:
class Abstract(ABC):
@property
@abstractmethod
def title(self) -> str: ...
class Concrete(Abstract):
title = "Test" # pylance error
Однако в Concrete
я получаю ошибку pylance:
Expression of type "Literal['Test']" cannot be assigned to declared type "property"
"Literal['Test']" is incompatible with "property"
Можно ли указать абстрактный атрибут в абстрактном родительском классе, чтобы его можно было переопределить буквальным значением в наследующем классе?
Итак, есть несколько разных вариантов в зависимости от конкретного использования. Если вы хотите определить тип атрибута в абстрактном классе, это можно сделать так:
class Abstract(ABC):
title: str
class Concrete(Abstract):
title = "test"
Предполагается, что значение title может меняться через Concrete
, а «test» — это просто значение по умолчанию.
Однако может оказаться, что вам нужна переменная класса. Это значение будет установлено только конкретным классом и не будет обновляться методами:
from typing import ClassVar
class Abstract(ABC):
title: ClassVar[str]
class Concrete(Abstract):
title = "test"
Подробности смотрите здесь. Обратите внимание, что ни один из этих параметров не требует переопределения атрибута, как и свойств, но это именно так и работает. Надеюсь это поможет!
Этот код относительно(?) хорош: вы объявляете протокол Abstract
, который, как минимум, требует атрибута title
только для чтения типа str
. Ваш Concrete
удовлетворяет этому требованию. Более того, это на самом деле известный рецепт.
Соответствующий pyright
билет проливает больше света на эту проблему. Разница заключается в доступе к классу: Abstract
означает, что доступ из класса даст объект property
, но Concrete
предоставляет простой str
. Это различие может быть важным в некоторых случаях самоанализа или метапрограммирования.
Я не согласен здесь с Эриком Траутом (сегодня открою заявку, чтобы подробнее рассказать об этом — спасибо, что обратили на это мое внимание!), потому что такое поведение, по-видимому, противоречит рекомендациям PEP-544 (с использованием базы Protocol
вместо ABC
в вашем примере не меняет вывод пирайта):
Чтобы определить переменную протокола, доступную только для чтения, можно использовать (абстрактное) свойство.
mypy
принимает ваш код как есть (см. рецепт выше и игровая площадка). Если вы хотите удовлетворить pyright
, вам следует либо переопределить свойство, либо добавить комментарий для игнорирования, либо поискать обходные пути. Я бы предпочел # type: ignore
прокомментировать исключительно потому, что считаю это ошибкой проверки типов, но решать вам.
Спасибо - есть надежда, что PEP 729 разрешит эти проблемы с типизацией разумным образом.