Skip to content

Python Typing constructs that are confusing

Rich Chiodo edited this page May 8, 2023 · 3 revisions

This is a list of typing contructs that could be confusing for people coming to Python typing from other languages:

Variance

This one is always easy to forget. It's rather long so it gets its own page:

https://github.com/microsoft/pylance-release/wiki/Covariance-and-Contravariance

Tuple type creation

Relevant documentation: https://docs.python.org/3/library/typing.html#typing.Tuple

Tuple types can have a number of forms.

x: Tuple[int] = (1)
x: Tuple[int, float] = (1, 2.0)
x: Tuple[int, ...] = ()

The weird one being the last one. That ... means zero or more ints in the tuple. However, it's only allowed once. This is illegal:

x: Tuple[int, int, ...] = (1)

If you wanted a tuple with 1 or more ints, you'd do the following instead:

y: Tuple[int, *Tuple[int, ...]] = (1,)

TypeVars aren't actually constructable

This example:

image

The intuition for TypeVar('d', bound=int) is that it's a type that's derived from int. Same with NewType('e', int).

But for some reason that's not true even though it behaves that way.

See https://peps.python.org/pep-0695/#upper-bound-specification

TypeVars are like templates being declared outside of their usage. They're not instantiable.

NewTypes are for enforcing an abstraction around another type. To make sure that that item is not just an alias. https://peps.python.org/pep-0484/#newtype-helper-function

For more information, see the advanced type topic for Pyright.

Pseudo 'parameters' in functions

https://peps.python.org/pep-0570/#syntax-and-semantics

def foo(x: int, y: int, /, pos_or_keyword_arg, *, **kwargs):
    pass

There are special indicators that separate positional and keyword only arguments. You can't pass anything for these parameters, it's just a way to indicate that in the function definition.

They do have a use though. Here's one such example:

https://github.com/microsoft/pylance-release/issues/2346#issuecomment-1031757771