Search code examples
python-3.xmypy

How can I narrow the Generic type in python3?


The Generic[T] allows any T, but I want the mypy errors out if the T is not subclass of some class. For example:

from enum import Enum
from typing import Dict, Generic, TypeVar

T = TypeVar("T")

class test_t(Generic[T]):
    data: T
    def __init__(self, data: T):
        self.data = data

... some magic code define GenericEnum ...

class test2_t(GenericEnum[T]):
    data: T
    def __init__(self, data: T):
        self.data = data

test_t[int](10) # this is fine
test2_t[int](10) # mypy should yield some error about the int is not compatible generic type

maybe I could subclass the Generic class and check the T with issubclass() during test_t[int]. But I do not know which method I should override, or if there is already some canonical way to do this


Solution

  • Generic is only a helper to make classes generic over some Type variables. In order to limit what a Type variable accepts the specific TypeVar has to be bounded or constrained.

    A bounded Type variable is limited to subclasses of the given type:

    from typing import Generic, TypeVar
    
    B = TypeVar("B", bound=str)
    
    class test_b(Generic[B]):
        def __init__(self, data: S):
            self.data = data
    
    test_b("yes!")
    test_b[int]  # error: Value of type variable "B" of "test_b" cannot be "int"
    test_b(12)   # error: Value of type variable "B" of "test_b" cannot be "int"
    

    When matching a subclass of the bound, the Type Var is inhabited by the most specific subclass. For example, when matching class String(str) as a subclass of str then B is inhabited by String.

    A constrained Type variable is limited to the given types:

    from typing import Generic, TypeVar
    
    C = TypeVar("C", str, list)
    
    class test_c(Generic[C]):
        data: C
        def __init__(self, data: C):
            self.data = data
    
    test_c("yes!")
    test_c(["y", 1, True])
    test_c[int]  #  Value of type variable "C" of "test_c" cannot be "int"
    test_c(12)   #  Value of type variable "C" of "test_c" cannot be "int"
    

    When matching a subclass of any constraint, the Type Var is inhabited by the respective constraint. For example, when matching class String(str) as a subclass of str then C is inhabited by str.