What the code '_T = TypeVar('_T')' means in a *.pyi file?
Asked Answered
P

1

14

I'm new to Python annotation (type hints). I noticed that many of the class definitions in pyi files inherit to Generic[_T], and _T = TypeVar('_T').

I am confused, what does the _T mean here?

from typing import Generic, TypeVar

_T = TypeVar('_T')

class Base(Generic[_T]): pass
Parthia answered 19/8, 2019 at 7:7 Comment(2)
_ is just means that that variable is protected type.Stieglitz
python does not support protections on variables like java but it is convention to use _ for developer conventionStieglitz
P
21

I recommend reading through the entire built-in typing module documentation.


typing.TypeVar

Basic Usage

Specifically, typing.TypeVar is used to specify that multiple possible types are allowed. If no specific types are specified, then any type is valid.

from typing import TypeVar

T = TypeVar('T')            # <-- 'T' can be any type
A = TypeVar('A', str, int)  # <-- 'A' will be either str or int

But, if T can be any type, then why create a typing.TypeVar like that, when you could just use typing.Any for the type hint?

The reason is so you can ensure that particular input and output arguments have the same type, like in the following examples.

A Dict Lookup Example

from typing import TypeVar, Dict
Key = TypeVar('Key')
Value = TypeVar('Value')

def lookup(input_dict: Dict[Key, Value], key_to_lookup: Key) -> Value:
    return input_dict[key_to_loopup]

This appears to be a trivial example at first, but these annotations require that the types of the keys in input dictionary are the same as the key_to_lookup argument, and that the type of the output matches the type of the values in the dict as well.

The keys and values as a whole could be different types, and for any particular call to this function, they could be different (because Key and Value do not restrict the types), but for a given call, the keys of the dict must match the type of the lookup key, and the same for the values and the return type.

An Addition Example

If you create a new TypeVar and limit the types to float and int:

B = TypeVar('B', float, int)

def add_x_and_y(x: B, y: B) -> B:
    return x + y

This function requires that x and y either both be float, or both be int, and the same type must be returned. If x were a float and y were an int, the type checking should fail.


typing.Generic

I'm a little more sketchy on this one, but the typing.Generic (links to the official docs) Abstract Base Class (ABC) allows setting up a Class that has a defined type hint. They have a good example in the linked docs.

In this case they are creating a completely generic type class. If I understand correctly, this allows using Base[AnyName] as a type hint elsewhere in the code and then one can reuse AnyName to represent the same type elsewhere within the same definition (i.e. within the same code scope).

I suppose this would be useful to avoid having to use TypeVar repeatedly, you can basically create new TypeVars at will by just using the Base class as a type hint, as long as you just need it for the scope of that local definition.

Priggery answered 16/5, 2020 at 2:3 Comment(2)
Does the first argument 'B' always have to match the variable name B? This is the case in every example I've seen but I haven't seen an explanation as to why this has to be the case, or whether it has to be the case at all.Manolete
Found the answer, yes it should be the same.Manolete

© 2022 - 2024 — McMap. All rights reserved.