Skip to main content

Python Type Hints

Type Hinting in Python

Type hinting is a formal solution for statically indicating the type of a value in Python code.

It was specified in PEP 484 and introduced in Python 3.5.

warning

Python's type hints are useful for the developer and are not used at runtime!

Python type hints are used only by the type control system you are using, such as in the editor or IDE.

With type hinting, developers can check the consistency of variables and their types throughout the code base. All this is done in ahead of time, i.e., before the code is executed.

Syntax of Type Hints in Python

Type hints in Python involve a colon and a type declaration after the first invocation of a name in a namespace. For example:

name: str
age: int

name = input("Your name?")
age = int(input("Your age?"))
warning

The initial declarations of names with type hints ensure that any subsequent use of those names in that namespace is checked against those types.

For example, this code would be invalid and the type checker would complain because name as an int is already declared, and input by default returns a string.

name:  int 
age: int

name = input("Your name?")
age = int(input("Your age?"))

Python type checking systems will attempt to infer types whenever possible.

In the following example, the type checker can infer that name is a string (because input() returns nothing else) and that age is a int (because int() returns nothing else).

name = input("Your name?") 
age = int(input("Your age?"))

Type Hints in Python functions

In Python, it is also possible to type hint functions to describe in advance the values they accept and return.

Consider the following example in which a

greeting = "Hi, {}, you are {} years old"

def greet(user, age):
return greeting.format(user, age)

name = input("Your name?")
age = int(input("Your age?"))

print(greet(name, age))
note

Note that a possible ambiguity is that greet() could in theory accept any types for user and age, and could return any type.

It is possible to disambiguate the code above with type hints in the following way:

greeting = "Hi, {}, you are {} years old"

def greet(user:str, age:int) -> str:
return greeting.format(user, age)

name = input("Your name?")
age = int(input("Your age?"))

print(greet(name, age))
note

Thanks to these type hints for greet(), your editor will know ahead of time which types greet() will accept when you call it in your code.

note

Python type checking systems will attempt to automatically infer what types are returned from a function.

But if you plan on using type hinting with a function, it’s better to hint everything about the function (included the return type!)

Type Hints in Python Objects

Since objects like lists, dictionaries, and tuples can contain other objects, we will sometimes want to type hint them to indicate what kinds of objects they contain.

For this we need to turn to Python’s typing module, which supplies tools for describing the types such things will hold.

For example, dictionaries are composed of keys and values among several types: you may describe the types for a dictionary by passing them to typing.Dict as a list. Similarly, you may specify the object type of a list by passing it to typing.List.

from typing import Dict, List

dict_of_users: Dict[int,str] = {
1: "Tom",
2: "Ryan"
}

list_of_users: List[str] = [
"Tom", "Ryan"
]

Type Hints in Python classes

To provide type hints for classes, simply refer to their names as you would any other type. For example:

from typing import Dict

class User:
def __init__(self, name):
self.name = name

users: Dict[int, User] = {
1: User("Serdar"),
2: User("Davis")
}

def inspect_user(user:User) -> None:
print (user.name)

user1 = users[1]
inspect_user(user1)
note

Note that inspect_user() has a return type of None due to the fact it only print()s output and does not return anything.

When using type hints for custom objects, we will sometimes need to provide a type hint for an object that has not been defined yet. In that case, you can use a string to provide the object name:

class User:
def __init__(self, name:str, address:"Address"):
self.name = name
self.address = address

class Address:
def __init__(self, owner:User, address_line:str):
self.owner = owner
self.address_line = address_line

Multiple Type Hints in Python Objects

Some objects may contain one of a couple of different types of objects. For this, you can use Union or Optional.

  • Use Union to indicate that an object can be one of several types.
  • Use Optional to indicate that an object is either one given type or None.

For example, consider a dictionary that takes integers (int) as keys but either integers (int) or strings (str) as values.

Union example
from typing import Dict, Optional, Union

dict_of_users: Dict[int, Union[int,str]] = {
1: "Tom",
2: "Ryan",
3: 23
}

Then, consider a variable user defined as below: it can be an int or None (but not a str!)

Option example
user_id: Optional[int]
user_id = None # valid
user_id = 3 # valid
user_id = "Hello" # not valid

Type Aliases

Python allows you to give a type an alias and use the alias for type hinting.

A type alias is defined by assigning the type to the alias.

For example, we can assign the Union[int, float] type an alias Number and use the Number alias in the sum() function.

from typing import Union

number = Union[int, float]

def sum(x: number, y: number) -> number:
return x + y

Type Hints for list, dictionary and set

There are built-in types to set type hints for list, dictionary and set.

warning

For example, you will get an error if you try to type hints a variable as a list but later assign a dictionary to it:

ratings: list = [1, 2, 3]
ratings = {1: 'Bad', 2: 'average', 3: 'Good'}
app.py:3: error: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "List[Any]")
Found 1 error in 1 file (checked 1 source file)

To specify the types of values in the list, dictionary, and sets, you can use type aliases from the typing module:

Type AliasBuilt-in Type
Listlist
Tupletuple
Dictdict
Setset
Frozensetfrozenset
SequenceFor list, tuple, and any other sequence data type.
MappingFor dictionary (dict), set, frozenset, and any other mapping data type
ByteStringbytes, bytearray, and memoryview types.

Style recommendations

The PEP 8 recommends the following style guidelines for type hinting:

  • Use the normal rules for colon, that is, no space before and one space after the colon (text: str).
  • Use spaces around the = sign when combining the annotation of an argument with a default value (align: bool = True).
  • Use spaces around the arrow -> (def headline(...) -> str).