# Adding type hinting to our application
Type hinting can be really useful, particularly in larger applications. It makes it clear what data types arguments to functions should be, and what data types functions are returning. Also, IDEs can greatly benefit from type hinting in order to offer better suggestions and finding potential errors in your code.
# Type hinting primer
# Type hinting parameters
This is how we add type hinting to function parameters:
def my_function(name: str, age: int): print(name, age)
: TYPE after each parameter, where
TYPE can be one of:
- A built-in data type, like
- Any class.
Often we use the
typing module to let us add more information. For example, if a function accepts a list of strings, we would do this:
from typing import List def my_function(names: List[str]): pass
Note the square brackets,
List[str], are not a typo!
Starting with Python 3.9, we will no longer need the
typing module for some of the most common builtin types, so we will be able to do
list[str] without having to import anything.
# Type hinting return values
To add type hinting for the return value of a function, we use
def sum(x: int, y: int) -> int: return x + y
# Adding type hinting to our code
Let's start with
database.py. I'll go through and add type hinting to the functions where possible.
psycopg2 does not support type hinting, so any
connection parameters will be left un-typed.
database.py often return relatively complex values, such as rows from a table. These can be type-hinted as tuples.
For example, a row from the
polls table has three values:
id, an integer
title, a string
owner_username, a string
Any function that returns rows from that table should be type-hinted like this:
from typing import List, Tuple ... def get_polls(connection) -> List[Tuple[int, str, str]]: with connection: with connection.cursor() as cursor: cursor.execute(SELECT_ALL_POLLS) return cursor.fetchall()
That can get quite tricky to read, so I usually extract compound types to variables at the top of the file.
I'll create a custom type variable for each result set that our database is getting.
Poll = Tuple[int, str, str] Vote = Tuple[str, int] PollWithOption = Tuple[int, str, str, int, str, int] PollResults = Tuple[int, str, int, float]
As a reminder, here's where they're used:
Pollis returned by the
Voteis returned by the
PollWithOptionis returned by the
PollResultsis returned by the
Then I'll go through the functions and add the return value type hints:
def get_polls(connection) -> List[Poll]: ... def get_latest_poll(connection) -> List[PollWithOption]: ... def get_poll_details(connection, poll_id: int) -> List[PollWithOption]: ... def get_poll_and_vote_results(connection, poll_id: int) -> List[PollResults]: ... def get_random_poll_vote(connection, option_id: int) -> Vote: ... def create_poll(connection, title: str, owner: str, options: List[str]): ... def add_poll_vote(connection, username: str, option_id: int): ...
Then I'll go over to
app.py and add the type hint in the one function that might use it:
from typing import List ... def _print_poll_options(poll_with_options: List[database.PollWithOption]): ...