Сопоставимые типы с mypy

Я пытаюсь создать общий класс, чтобы выразить, что значение имеет нижнюю и верхнюю границы, и обеспечить соблюдение этих границ.

from typing import Any, Optional, TypeVar

T = TypeVar("T")

class Bounded(object):
    def __init__(self, minValue: T, maxValue: T) -> None:
        assert minValue <= maxValue
        self.__minValue = minValue
        self.__maxValue = maxValue

Однако mypy жалуется, что:

error: Unsupported left operand type for <= ("T")

Очевидно, модуль набора текста не позволяет мне выразить это (хотя похоже, что добавление Comparable может случится в будущем).

Думаю, было бы достаточно проверить, есть ли у объекта методы __eq__ и __lt__ (по крайней мере, для моего случая использования). Есть ли в настоящее время способ выразить это требование в Python, чтобы Mypy его понял?


person Hannes Karppila    schedule 25.12.2017    source источник


Ответы (1)


После небольшого исследования я нашел решение: протоколы. Поскольку они не полностью стабильны (пока что Python 3.6), их необходимо импортировать из модулей typing_extensions.

import typing
from typing import Any
from typing_extensions import Protocol
from abc import abstractmethod

C = typing.TypeVar("C", bound="Comparable")

class Comparable(Protocol):
    @abstractmethod
    def __eq__(self, other: Any) -> bool:
        pass

    @abstractmethod
    def __lt__(self: C, other: C) -> bool:
        pass

    def __gt__(self: C, other: C) -> bool:
        return (not self < other) and self != other

    def __le__(self: C, other: C) -> bool:
        return self < other or self == other

    def __ge__(self: C, other: C) -> bool:
        return (not self < other)

Теперь мы можем определить наш тип как:

C = typing.TypeVar("C", bound=Comparable)

class Bounded(object):
    def __init__(self, minValue: C, maxValue: C) -> None:
        assert minValue <= maxValue
        self.__minValue = minValue
        self.__maxValue = maxValue

И Mypy доволен:

from functools import total_ordering

@total_ordering
class Test(object):
    def __init__(self, value):
        self.value = value
    def __eq__(self, other):
        return self.value == other.value
    def __lt__(self, other):
        return self.value < other.value

FBounded(Test(1), Test(10))
FBounded(1, 10)
person Hannes Karppila    schedule 25.12.2017
comment
Это очень смело - person pseudosudo; 17.06.2020
comment
примечание: здесь тот же код: github.com/python/typing/issues/59# issuecomment-353878355 - person David Froger; 30.12.2020