Internally Python uses classes and therefore types everywhere. However, when we declare our methods, we omit the types and end up with limited functionality for our code editors. In this post we look at type hints and how they can improve our experience to write code.
This post is part of my journey to learn Python. You find the code for this post in my PythonFriday repository on GitHub.
Type hints?
Python offers type hints since version 3.5 and they are meant to be hints:
The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.
The basic example from the documentation uses this code snipped to show you the syntax:
1 2 |
def greeting(name: str) -> str: return 'Hello ' + name |
The greetings() function accepts the parameter name of the type string and returns a string.
The benefits
If we add the example from above with the type hints into a module called with_types.py, we get this support in VS Code:
However, if we put our method from above into a module called without_types.py and omit the type hints, we get only this support in VS Code because it knows nothing about a being a string:
What types can we use?
The “Type hints cheat sheet” offers examples for most use cases. I only picked the ones I find the most useful for Python 3.11+:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from typing import Callable, Iterator, Union, Optional, Any # one parameter with a type def stringify(num: int) -> str: return str(num) # multiple parameters with types def plus(num1: int, num2: int) -> int: return num1 + num2 # return type can differ from types of parameters def division(num1: int, num2: int) -> float: return num1 / num2 # lists can have a type def total(values: list[int]) -> int: return sum(values) # for dictionaries we can specify the type of the key and of the value def total_values(input: dict[str, float]) -> float: return sum(input.values()) # touples with variable size but same type can use ... def print_tuple(numbers: tuple[int, ...]) -> None: for number in numbers: print(number) # booleans can be used as type def is_working(a: bool, b: bool) -> bool: return a & b |
How to handle special cases?
If our parameter could hold a string or None, we can declare it with the Optional keyword:
1 2 3 |
def hello(name: Optional[str]) -> None: if name: print(name) |
If we can accept multiple types, we can use the | character to separate the different type hints:
1 2 3 |
def printer(values: list[int | str]) -> None: for i in values: print(i) |
If you do not care about the type but you need to define something, you can use Any -> None:
1 2 |
def whatever(input: Any): pass |
Conclusion
Type hints are optional and only hint at the type. They offer us some great help while we write code and make it clearer what type we expect in our functions. There are tools like Pydantic that leverage the type hints and give us validation and fitting error messages for no additional cost.
We are revisiting this topic when we explore FastAPI, a framework that is built on top of type hints.
1 thought on “Python Friday #207: Type Hints”