pythontype-hinting

Type hint for "can be compared" objects


I am writing several functions that handle ordered datasets. Sometime, there is an argument that can be a int or float or a timestamp or anything that supports comparison (larger than / smaller than) and that I can use for trimming data for instance.

Is there a way to type-hint such a parameter? The typing module doesn't seem to include this, but is there some other way?


Solution

  • There is no standard 'comparable' ABC, no, as the rich comparison methods are really very flexible and don't necessarily return booleans.

    The default built-in types return NotImplemented when applied to a type they can't be compared with, for example, while specialised libraries like SQLAlchemy and numpy use rich comparison methods to return completely different objects. See the documentation for the rich comparison methods for the details.

    But you should be able to define a a Protocol subclass for specific expectations:

    from typing import Protocol, TypeVar
    
    T = TypeVar("T", infer_variance=True)
    
    class Comparable(Protocol[T]):
        def __lt__(self: T, other: T) -> bool:
            ...
    
        # ... etc
    

    You may need to tweak the protocol to fit your exact expectations, and / or use a non-generic version that's specific to the types you use (perhaps with @overloaded definitions for specific types).

    For sorting with the builtin sorted() function, just having a __lt__ method should suffice that accepts any value to compare with.

    The typeshed project defines a SupportsRichComparison type alias and SupportsRichComparisonT typevar bound to the type alias, which is defined as the union of two such protocols:

    class SupportsDunderLT(Protocol[_T_contra]):
        def __lt__(self, other: _T_contra, /) -> bool: ...
    
    class SupportsDunderGT(Protocol[_T_contra]):
        def __gt__(self, other: _T_contra, /) -> bool: ...
    
    SupportsRichComparison: TypeAlias = SupportsDunderLT[Any] | SupportsDunderGT[Any]
    SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison)
    

    The built-in sorted() function is annotated as accepting Iterable[SupportsRichComparisonT] as its first argument, returning list[SupportsRichComparisonT].

    You can import these from the _typeshed package, provided you import them behind a TYPE_CHECKING guard. This works because type checkers include the typeshed types by default when checking:

    from typing import TYPE_CHECKING
    
    if TYPE_CHECKING:
        from _typeshed import SupportsRichComparisonT