ignoring_gravity
ignoring_gravity

Reputation: 10476

Type of field dependent on type of other field

Is it possible to create a class like

from typing import Union, Literal

class Foo:
    bar: Union[str, int]
    qux: Literal["str", "int"]

such that, if qux is Literal["str"], then bar is of type str, and if qux is Literal["int"], then bar is of type int? Is it possible to annotate that?

I'm aware of typing.overload, but I don't think it's relevant for this example

Upvotes: 4

Views: 814

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 50076

Dependent types are not generally supported by Python's typing system. However, it is possible to emulate some specific cases.

For a low number of dependent types, one can enumerate the cases. This requires making the individual types generic:

from typing import Union, Literal, Generic, TypeVar

Bar = TypeVar("Bar", str, int)
Qux = TypeVar("Qux", Literal["str"], Literal["int"])


class GenericFoo(Generic[Bar, Qux]):
    bar: Bar
    qux: Qux

    # not always needed – used to infer types from instantiation
    def __init__(self, bar: Bar, qux: Qux): pass

It is then possible to define the dependency either

  • as a Union of the cases:
    Foo = Union[GenericFoo[str, Literal["str"]], GenericFoo[int, Literal["int"]]]
    
    f: Foo
    f = GenericFoo("one", "str")
    f = GenericFoo(2, "int")
    f = GenericFoo("three", "int")
    
  • by overloading the instantiation:
    class GenericFoo(Generic[Bar, Qux]):
        bar: Bar
        qux: Qux
    
        @overload
        def __init__(self, bar: str, qux: Literal["str"]):
            pass
    
        @overload
        def __init__(self, bar: int, qux: Literal["int"]):
            pass
    
        def __init__(self, bar: Bar, qux: Qux):  # type: ignore
            pass
    

Upvotes: 5

Related Questions