Search code examples
pythonmypytyping

bounds with typyvar


I want to make sure that the input is the same as output, I tried to use TypeVar to bound the typing but I can't get it to work...

from typing import TypeVar, Union, Any
import pandas as pd

class my_Class():
    def __init__(self):
        self.days = []
        self.count = 0

    def __call__(self, 
                 input: Union[dict, list[dict]], 
                 *args, 
                 **kwargs
                 ) -> Union[dict, list[dict]]:
        "some code"

what I tried:

from typing import TypeVar, Union, Any
import pandas as pd

T = TypeVar("T", bound=Union[dict, list[dict]])

class my_Class():
    def __init__(self):
        self.days = []
        self.count = 0
    def __call__(self, input: T, *args, **kwargs) -> T:
        "some code"

It's saying that it's expecting "T" and got either Dict or List but I thought since I bounded it, it will look into that Union that I defined in T? Can someone direct me to what I'm doing wrong here?


Solution

  • You need a constrained type variable, not a bound type variable, in order to require that the same type be bound to T in both cases.

    from typing import TypeVar
    
    T = TypeVar('T', pd.DataFrame, dict, list[dict])
    
    class MyClass:
        def __init__(self):
            self.days = []
            self.count = 0
    
        def __call__(self, input: T, *args, **kwargs) -> T:
            ...
    

    With a union bound, both the argument and the return type are restricted to one of the three given types, but each use of T can be bound independently of the other. Further, you have no idea, statically speaking, which of the three will be returned. With a constrained type variable, the same type has to be bound to T in every occurrence within a single call to __call__.


    Here's a simple example:

    from typing import Generic, TypeVar
    
    T = TypeVar('T', int, str)
    
    class Foo:
        def __call__(self, x: T) -> T:
            return x
    
    x: int = Foo()(9)  # OK, 9 makes return value int
    y: str = Foo()("foo")  # OK, "foo" makes return value str
    z: float = Foo()(3.14)  # Fails, 3.14 not an int or str
    w: str = Foo()(9)  # Fails, can' assign an int to w