Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between a constrained TypeVar and a Union?

Tags:

If I want to have a type that can be multiple possible types, Unions seem to be how I represent that:

U = Union[int, str] 

U can be an int or a str.

I noticed though that TypeVars allow for optional var-arg arguments that also seem to do the same thing:

T = TypeVar("T", int, str)

Both T and U seem to only be allowed to take on the types str and int.

What are the differences between these two ways, and when should each be preferred?

like image 775
Carcigenicate Avatar asked Nov 17 '19 18:11

Carcigenicate


People also ask

What is TypeVar?

In short, a TypeVar is a variable you can use in type signatures so you can refer to the same unspecified type more than once, while a NewType is used to tell the type checker that some values should be treated as their own type.

What is union type in Python?

Union type; Union[X, Y] is equivalent to X | Y and means either X or Y. To define a union, use e.g. Union[int, str] or the shorthand int | str . Using that shorthand is recommended.

What are type hints?

Introduction to Python type hints It means that you need to declare types of variables, parameters, and return values of a function upfront. The predefined types allow the compilers to check the code before compiling and running the program.

What is type annotation in Python?

Type annotations — also known as type signatures — are used to indicate the datatypes of variables and input/outputs of functions and methods. In many languages, datatypes are explicitly stated. In these languages, if you don't declare your datatype — the code will not run.


1 Answers

T's type must be consistent across multiple uses within a given scope, while U's does not.

With a Union type used as function parameters, the arguments as well as the return type can all be different:

U = Union[int, str]

def union_f(arg1: U, arg2: U) -> U:
    return arg1

x = union_f(1, "b")  # No error due to different types
x = union_f(1, 2)  # Also no error
x = union_f("a", 2)  # Also no error
x # And it can't tell in any of the cases if 'x' is an int or string

Compare that to a similar case with a TypeVar where the argument types must match:

T = TypeVar("T", int, str)

def typevar_f(arg1: T, arg2: T) -> T:
    return arg1

y = typevar_f(1, "b")  # "Expected type 'int' (matched generic type 'T'), got 'str' instead
y = typevar_f("a", 2)  # "Expected type 'str' (matched generic type 'T'), got 'int' instead

y = typevar_f("a", "b")  # No error
y  # It knows that 'y' is a string

y = typevar_f(1, 2)  # No error
y  # It knows that 'y' is an int

So, use a TypeVar if multiple types are allowed, but different usages of T within a single scope must match each other. Use a Union if multiple types are allowed, but different usages of U within a given scope don't need to match each other.

like image 154
Carcigenicate Avatar answered Oct 05 '22 06:10

Carcigenicate