Mypy: универсальный контейнер с некоторыми методами действителен только в том случае, если применяются дополнительные протоколы

#python #types #mypy

#python #типы #mypy

Вопрос:

В mypy, как бы вы указали, что у типа Generic over T есть методы, которые допустимы только при T соблюдении определенных условий?

Например, если мы создали пользовательский класс коллекции с min помощью метода, возвращающего наименьший элемент в коллекции:

 from typing import Generic, TypeVar

T = TypeVar("T")

class MyCollection(Generic[T]):
    
    def __init__(self, some_list: List[T]):
        self._storage = some_list

    def min(self) -> T: # This requires that T implements __lt__
        "Get the smallest element in the collection"
        return min(self._storage)     
 

Как вы можете сообщить системе типов, что вызов min MyCollection of T разрешен только в случае T реализации __lt__ ?

Поэтому в принципе я хотел бы, чтобы некоторые методы универсального контейнера были действительны только при соблюдении дополнительных протоколов.

— Полезные ссылки —

Вы можете видеть из указателей типов в standardlib для min, что они определили протокол для принудительного __lt__ применения. реализован

 class SupportsLessThan(Protocol):
    def __lt__(self, __other: Any) -> bool: ...

SupportsLessThanT = TypeVar("SupportsLessThanT", bound=SupportsLessThan)  # noqa: Y001
 

Комментарии:

1. Я не думаю, что система типов python поддерживает это

2. Просто для ясности: вы хотите, чтобы весь класс принимал только T те, которые поддерживают < , или вы хотите, чтобы только конкретный метод принимал только такие T ? В последнем случае вы ожидаете, что метод будет полностью отсутствовать или просто не вернется (вызовет ошибку)?

Ответ №1:

В том же файле-заглушке, который вы связали, посмотрите подсказки по типу для list.sort :

 class list(MutableSequence[_T], Generic[_T]):
    ...
    @overload
    def sort(self: List[SupportsLessThanT], *, key: None = ..., reverse: bool = ...) -> None: ...
    @overload
    def sort(self, *, key: Callable[[_T], SupportsLessThan], reverse: bool = ...) -> None: ...
 

С помощью подсказки типа self вы можете указать, что метод применим только для определенных специализаций универсального класса. Вы также можете увидеть это задокументировано в документах mypy.

min Таким образом, ваш будет помечен как

 def min(self: 'MyCollection[SupportsLessThanT]') -> SupportsLessThanT:
    ...
 

с подходящим определением SupportsLessThanT .