7 minutes read

When programming in Python, the variable type doesn't need to be specified at creation. Instead, the data type is determined during runtime, making programming more accessible and leading to potential bugs that can be challenging to detect and fix.

Python 3.5 introduced type hints (PEP 484 and PEP 483) to address a common issue, and this feature has since been improved in newer versions. Type hints enable developers to specify the expected data types of variables, function arguments, and function return values and then check them. This is especially beneficial because Python doesn't perform any type checking at runtime, and type hints provide an effortless way to perform static analysis and runtime type checking.

In this topic, you will learn more about type hints and how to implement them in your programs.

Using type hints

Type hints can be used for variables, function arguments, and function return values.

Let's look at how we can use type hints on variables first. To use type hints on variables, use a colon : followed by the data type the variable is supposed to store. While multiple data types can be specified for the same variable, we will learn how to do it later. Here is an example of using type hints for different variables:

#type hint integer
i:int = 7

#type hint floating point
p:float = 2.5

#type hint boolean
b:bool = true

#type hint string
s:str = "Hello"

Next, let's look at how to use type hints for function arguments. This syntax is essentially the same as the syntax for the variable-type hints. Use a colon : followed by the required data types. Here is the syntax and examples of using type hints for function arguments.

#syntax for implementation of type hints for function arguments
def function(arg1:int, arg2:str, arg3:float)
    
#examples of type hint implementation for function arguments
def employee(id:int, name:str, salary:float)
def resultsheet(id:int, name:str, maths:int, science:int, gpa:float, result:bool)

Finally, you will learn how to specify what type of value the function will return using type hints. To do this, we use ->, followed by the data types the function is supposed to return. Take a look at the syntax:

def function(arg1:int, arg2:str, arg3:float) -> str

def employee(id:int, name:str, salary:float) -> bool
def resultsheet(id:int, name:str, maths:int, science:int, gpa:float, result:bool) -> str

Why type hints?

Type hint allows you to understand function signatures (argument types, return type), can help you run static checkers on your code, and can increase code readability. When you implement type hints in your code, you specify:

  • types of data you want a variable to store
i:int=4
  • the types of arguments you pass to a function
def divide(a: int|float,b: int|float):
  • or the type of values to return
def divide(a,b) -> int|float:

Doing this will make your code more readable and allow people to understand how to implement your functions easily.

To understand the last reason why type hints were introduced, let's look at an issue that has plagued Python since the very beginning:

i,j=4,2
k=i/j
print(k)

The output for the given program is:

2.0

This program divides two numbers. However, two values need to be a number. If a string replaces the number, for example:

i,j=4,"2"
k=i/j
print(k)

The program gives an error message:

Traceback (most recent call last):
  File "<file_path>", line 2, in <module>
    k=i/j
TypeError: unsupported operand type(s) for /: 'int' and 'str'

This problem results from the dynamic nature of Python and the fact that Python does not check variable data types during runtimes. The program, therefore, does not know the data type of variables till runtime. This allows such errors to occur during program execution.

If there were a way to find these errors during compilation, such problems would never arise. And that is why type hints were introduced.

Type hints in action

Let's look at the problem we discussed earlier.

i,j=4,"2"
k=i/j
print(k)

In this program, the variables i and j should only store numerical values int or float. To make it easier to understand, let's use int for i and j, and use float for k. After implementing type hints, the program will look something like this.

i:int=4
j:int="2"
k:float=i/j
print(k)

We can now run the program since we have used type hints in our code. When this program starts, we get the following error message:

Traceback (most recent call last):
  File "<file path>", line 3, in <module>
    k:float=i/j
TypeError: unsupported operand type(s) for /: 'int' and 'str'

Process finished with exit code 1

Oops, something went wrong! Why is it there? The error message is the same as the previous one. Did the type hint do anything at all?

Well, no! When a Python code runs, the type hint is ignored. So, type hints won't magically convert incorrectly placed values and correct them. So why are we using type hints here?

Type hints check whether the data used is the correct data type or not. This is done before program execution. For this, we need to use a static type checker. Mypy is the most popular type checker; we will use it for the following examples. However, before you can use mypy, you need to install it first. Execute the following command in your console.

pip install mypy

This code will automatically download the latest version of mypy and can be used right after installation. After that, we can use it to check whether the type hint assignments are compatible or not:

i:int=4
j:int="2"
k:float=i/j
print(k)

Before we run it, use mypy to check the type of assignments. To do this, we need to use the console command

mypy main.py 

We get the following error message.

main.py:2: error: Incompatible types in assignment 
(expression has type "str", variable has type "int") [assignment]
Found 1 error in 1 file (checked 1 source file)

Great! Mypy has successfully checked the program and revealed the discrepancy in the type of assignment. So we know there is an issue before the program runs, and we can fix the problem.

i:int=4
j:int=2
k:float=i/j
print(k)

After correction, we can now recheck the program:

mypy main.py
Success: no issues found in 1 source file

Excellent, there are no issues. You can finally run the program and get the output.

2.0

Multiple data types

In the previous example,

i:int=4
j:int=2
k:float=i/j
print(k)

i and j could be floating point numbers. Python is a dynamically typed language, and a variable should be allowed to store multiple data types in Python. Therefore, there is a way to specify multiple data types using type hints.

In earlier versions of Python (3.5–3.9), this was achieved using union:

from typing import Union 
i:Union[int,float]=4.5
j:Union[int,float]=1.5
k:Union[int,float]=i/j
print(k)

Note the following line

from typing import Union

With Python 3.10 onwards, you can also use the bitwise or operator (|) to achieve this,

i:int|float=4.5
j:int|float=1.5
k:int|float=i/j
print(k)

After specifying multiple types of variables, let's move on to functions. Let's implement type hints for the arguments of the function first. You can do this using the same method used for variables.

#Python 3.10 onwards
def divide(a: int|float, b: int|float): # variable a and b can be int or float
    return a / b

x, y = 5, 2
z = divide(x, y)
print(z)

#Python 3.5-3.9
from typing import Union
def divide(a: Union[int, float], b: Union[int, float]): # variable a and b can be int or float
    return a / b

x, y=5, 2
z = divide(x, y)
print(z)

Finally, to specify the return types for the functions, we use the same syntax, but after ->.

#Python 3.10 onwards
def divide(a, b) -> int|float: # the return value of the function can be int or float
    return a / b

x, y = 5, 2
z = divide(x, y)
print(z)

#python 3.5-3.9
from typing import Union
def divide(a, b) -> Union[int, float]: # the return value of the function can be int or float
    return a / b

x, y=5, 2
z = divide(x, y)
print(z)

You can specify multiple data types for variables, function arguments, and return values.

Conclusion

Python is a dynamically typed language, and the developers of Python intend to keep it that way. Therefore, to fight the issues caused due to the dynamic nature of Python without resorting to using the static declaration of variable type, Python 3.5 introduced type hints to allow programmers to run static checkers on your code like mypy. They can also help understand function signatures (argument types, return type) and increase code readability.

In this topic, you also learned the following:

  • To implement type hints for variables variable_name: data_type
  • To implement type hints on functions def function_name(arg1:data_type1, arg2:data_type2, ...) -> return_data_type:
  • To install mypy using the console with the command pip install mypy
  • To use a static type checker mypy using the console command mypy file_name.py to check whether type hint assignments are compatible or not
  • To specify multiple data types using type hints for earlier and current Python versions.
26 learners liked this piece of theory. 1 didn't like it. What about you?
Report a typo