Search code examples
pythontypestype-hintingpython-dataclasses

Avoiding type warning for lists of sortable elements


I've got a list of elements of a dataclass X (marked order=True) and pass them to max(). I get a type checking warning in my IDE: Expected type 'Iterable' (matched generic type 'Iterable[SupportsLessThanT]'), got 'List[X]' instead. How can I avoid this error? What do I have to declare to make the warning disappear? (I don't want to suppress it of course.)

I thought because the class X is marked as order=True it would be obviously sortable, so that passing it to max() should be no problem. But apparently this isn't known to the type system.

@dataclass(frozen=True, order=True)
class X:
    value: int

def f(xs: List[X]) -> None:
    q = max(xs)  # here's the above mentioned type checking warning

I tried inheriting various things in the class X but nothing helped.

Is there a supposed way to deal with this or do I have to ignore the warning?


Solution

  • The code is fine as is. This is purely an issue of the type checker, in this case PyCharm/Intellij. Other type checkers such as MyPy correctly understand that the dataclass supports ordering.

    # so.py
    from typing import List
    from dataclasses import dataclass
    
    
    @dataclass(frozen=True, order=True)
    class X:
        value: int
    
    def f(xs: List[X]) -> None:
        q = max(xs)
    
    python -m mypy so.py --strict
    Success: no issues found in 1 source file
    

    Since the builtin PyCharm/Intellij type checker is rarely up to date, it can be worthwhile not to treat it as authoritative. Rely on a second type checker and ignore the issue until the builtin type checker is updated.

    If you want to help the builtin type checker, you can manually define a Protocol to express "this type is orderable". Inheriting the dataclass from this Protocol marks it as orderable.

    from typing import List, Protocol
    from dataclasses import dataclass
    
    class Orderable(Protocol):
        def __lt__(self, other) -> bool:
            ...
        def __gt__(self, other) -> bool:
            ...
        # add more comparison methods as desired
    
    @dataclass(frozen=True, order=True)
    class X(Orderable):
        value: int
    
    def f(xs: List[X]) -> None:
        q = max(xs)  # fine for PyCharm/Intellij