Python uses dynamic typing, which is sometimes a pain. You can't specify explicit types for variables - but you can utilize typing nevertheless

What are Python type hints?

Type hints in Python specify the type of variables or functions parameters as well as the return type. You can read more about them in the PEP 484 Guidelines.

In general they work like type declarations in every typed programming language, like Java or C++. The only difference is that type hints are completely optional. Python remains a dynamic language, because the Interpreter simply ignores the type hints. They are only relevant for development and have no influence on the execution. Accordingly, you can run a Python program with completely incorrect type hints without getting any errors.

Type hints exist just for you as a developer to make your life easier - so you should use them!

Types in Python

Python has many built-in types, way more than you might have expected. Some common examples for build-in types are str for strings, int for integers and bool for boolean values.

Furthermore, types are objects like everything else in Python. By the way, you can actually use types as keys for a dictionary. So the following code really works!

types_mapping = {
int: "Integer",
str: "String",
bool: "Boolean",
}
print(types_mapping[int])

Okay, you can do lots of things with the build-in types, but they are not really specific. For instance, you can have a variable of the type list, but what's actually in the list? Nobody knows.

That's why Python 3.5 introduced the typing module! You can import various types from this module, which you can specify really precisely. The following examples show some features of the typing module, but feel free to take some time and explore the module on your own.

How do type hints look like?

The syntax for type hints is, like the Python syntax in general, very simple.

In the following code example, you can see some type specifications for variables. The according syntax is just a colon after the variable name followed by the type.

# Type Hints with build-in types
age: int = 42
names: list = ["Alice", "Bob"]
translations: dict = {1: "one", 2: "two"}
are_type_hints_great: bool = True
# Type Hints with the typing module
from typing import List, Tuple, Callable
names: List[str] = ["Alice", "Bob"]
translations: dict = {1: "one", 2: "two"}
fruits: Tuple[str] = ("apple", "banana", "kiwi")
my_lambda: Callable = lambda name: f"Hello {name}"
# Type Hints using self defined classes
class Dog:
pass
alice: Dog = Dog()

For these examples the types might seem a bit redundant and obvious. In fact, you will use this kind of Type Hint rather seldom in comparison to the others. But when you use the returned object of a function for instance, you may not know the type of it.

More frequently used are type hints for parameters, and they basically work just the same as for variables. But note that this applies for parameters with default arguments too!

from typing import List, Tuple, Dict
class Car:
pass
def too_many_parameters(name: str, amount: float = 6.66, the_number: int = 42):
pass
def more_parameters(scores: List[int], persons: Dict[str, Tuple[str, int]]):
pass
def and_even_more_parameters(police_car: Car, racing_cars=List[Car]):
pass

Another possibility to specify type hints is for the return type of a function. There is also another syntax for this case: You specify the return type with an "arrow", followed by the type. It is important that the type is in the function definition before the colon.

from typing import List, Tuple
class House:
pass
def calculate_average(numbers: List[int]) -> float:
pass
def get_all_customer_names() -> List[str]:
pass
def get_my_house() -> House:
pass
def get_my_neighbours() -> Tuple[House]:
pass

This is the complete syntax you need to know if you want to use type hints! Easy, isn't it?

Benefits of specifying types

Some IDEs like PyCharm support type hints in different ways. First of all, PyCharm complains when a type hint does not matches with your coding. Additionally, you get a suggestion what the type should be or what is expected. This prevents unintentional behaviours where you expect a type and are surprised when you get something completely different. With type hints, you don't have to reengineer every function you want to call just for knowing what it will return anymore. In most cases, the combination of the function name and the type specifications make it clear how to use the function. If it doesn't, you should improve the readability of your code, for example by applying the KISS Principle or giving Meaningful Names.

Another really useful feature is the auto-completion! A common use-case for this scenario is an instance object from a class with some attributes and methods.

class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} barks!")
def get_dog_from_name(dog_name):
return Dog(dog_name)
alice = get_dog_from_name("Alice")
alice.bark()

If you call a function which returns an object, you might store it in a variable and work with it. Without type hints, the IDE wouldn't know what alice is. If you type a point after the name, you won't get any useful suggestions for functions or attributes of the Dog class. This changes when you add type hints!

class Dog:
def __init__(self, name: str):
self.name = name
def bark(self):
print(f"{self.name} barks!")
def get_dog_from_name(dog_name: str) -> Dog:
return Dog(dog_name)
alice = get_dog_from_name("Alice")
alice.bark()

Now you will get all possible suggestions for working with the class instance.

As we have seen, type hints are a really powerful tool to increase your productivity and motivation. It also greatly improves the readability of your code as everyone can quickly see what kind of objects you are working with. So all in all, you should definitely use type hints!