Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare Python constraint on generic type to support __lt__?

In the Python 3.5 code below, I want to use a less than operator (<) to compare two generic values. How can I declare a constraint on T to support __lt__?

from typing import *
import operator 

T = TypeVar('T')

class MyList(Generic[T]):
    class Node:
        def __init__(self, k:T) -> None:
            self.key = k 
            self.next = None  # type: Optional[MyList.Node]

    def __init__(self) -> None:
        self.root = None # type: Optional[MyList.Node]

    def this_works(self, val:T) -> bool:
        return self.root.key == val 

    def not_works(self, val:T) -> bool:
        return operator.lt(self.root.key, val)

I'm using Mypy to type check and it's failing on not_works with the message:

$ mypy test.py
test.py: note: In member "not_works" of class "MyList":
test.py:20: error: Unsupported left operand type for < ("T")

Other languages support constraints on T.

In C#: class MyList<T> where T:IComparable<T>

In Java: class MyList<T extends Comparable<? super T>>

like image 433
projectshave Avatar asked Sep 25 '15 17:09

projectshave


People also ask

Is generic data type supported in Python?

Generics are not just used for function and method parameters. They can also be used to define classes that can contain, or work with, multiple types. These “generic types” allow us to state what type, or types, we want to work with for each instance when we instantiate the class.

How do you write generic codes in Python?

Generic functions: from typing import TypeVar, Sequence T = TypeVar('T') # Declare type variable def first(seq: Sequence[T]) -> T: return seq[0] def last(seq: Sequence[T]) -> T: return seq[-1] n = first([1, 2, 3]) # n has type int.

What is generic type constraint?

A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)


1 Answers

You can achieve your goal by passing an extra parameter bound to TypeVar, as described in PEP484:

A type variable may specify an upper bound using bound=<type>. This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subtype of the boundary type. A common example is the definition of a Comparable type that works well enough to catch the most common errors:

Sample code from mentioned PEP:

from typing import TypeVar

class Comparable(metaclass=ABCMeta):
    @abstractmethod
    def __lt__(self, other: Any) -> bool: ...
    ... # __gt__ etc. as well

CT = TypeVar('CT', bound=Comparable)

def min(x: CT, y: CT) -> CT:
    if x < y:
        return x
    else:
        return y

min(1, 2) # ok, return type int
min('x', 'y') # ok, return type str

In latest version (verified with 0.521) of mypy, the scenario above is correctly handled.

like image 187
Yakuza Avatar answered Sep 28 '22 06:09

Yakuza