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.
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?"))
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 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))
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.
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 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 orNone
.
For example, consider a dictionary that takes integers (int
) as keys but either integers (int
) or strings (str
) as
values.
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
!)
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.
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 Alias | Built-in Type |
---|---|
List | list |
Tuple | tuple |
Dict | dict |
Set | set |
Frozenset | frozenset |
Sequence | For list , tuple , and any other sequence data type. |
Mapping | For dictionary (dict ), set , frozenset , and any other mapping data type |
ByteString | bytes , 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
).