Type Hints

All values in Python have a type, e.g., 6 has type int (short for integer), and 'seven' has type str (short for string). 6.7 is a float, short for floating point, which means a number that has a decimal point. The built-in type function returns the type of its argument.

>>> type(1)
<class 'int'>
>>> type('seven')
<class 'str'>
>>>> type(lambda x: x + 1)
<class 'function'>

Python is dynamically typed, which means that names (also known as variables) do not have types. A name can be assigned to a value of one type and later assigned to a value of a different type:

>>> x = 6
>>> x = 'seven'

This behavior contrasts with statically typed languages such as Java, in which variables have fixed types.

When you're writing code, it's often useful to give a hint to readers about what type of value a name is expected to have. If you're writing a function that will be used by others, describing the expected type of each parameter and the return value is helpful documentation. Type checking, which is a process that ensures that the values assigned to variables have the expected type, can also be very useful for finding bugs. As a result of these benefits, Python programs commonly include type hints, which allow you to describe the expected type for variables in your code.

A type hint for a single variable looks like a colon after the variable name, followed by the expected type:

x: int = 6
y: string = "seven"
z: float = 1.5

You can also add type hints for the parameters and return value of a function:

def fibonacci(n: int) -> int:
   ...

In fibonacci above, The input parameter n is expected to have type int. The arrow (->) is part of the type hint for the return value, and -> int means that this function is expected to return an int.

A type hint for a list can just be list, but if the items in the list are all meant to have the same type, then the hint can include the type of the items as well. For example, list[int] describes a list of integers, and list[str] is a list of String values.

s: list = [1, 'two', 3.0]
t: list[int] = [4, 5]
u: list[float] = [6.7]

Python's interpreter does not enforce that a name can only be bound to values that have the type in the hint, so this code will run without an error:

>>> x: int = 'not an int'
>>> x
'not an int'
>>> type(x)
<class 'str'>

Above, when we call type(x), we're not finding the type of the variable x because variables don't have types! Instead, we're finding the type of the value of x. That's because to evaluate a call expression, Python evaluates the operands before applying the function. So, to evaluate type(x), Python first evaluates x to the string 'not an int', and then takes the type of 'not an int'.

Given that Python doesn't enforce type hints when running a program, you may be wondering why they're useful at all! One reason is that type hints are great documentation. They're also useful because type checkers like pyright and mypy will check the types in a program and tell you when a type hint won't match the actual value type. Type checking is often a useful way to find bugs.

We won't always use type hints for code in this class, and we will not enforce that your code uses type hints. We will try to use them in places where we think they will be helpful to you! You'll also notice them commonly in Python code you find on the web.