-
The Usage of Type Hints
Since in version 3.5 Python the Type Hints were implemented, in this very short article I want to go through the idea behind it.
Let's say you need to create a function that removes values from the given list. The solution you may come up with looks like this.
def remove_all_values(els, val): while True: try: els.remove(val) except ValueError: return els assert remove_all_values([1, 2, 3, 3, 3, 3], 3) == [1, 2]
Given that someone else might use this function you may want to add a couple lines of comments.
def remove_all_values(els, val): ''' removes all val from els and returns the result. '''
This way it is more obvious how each argument is going to be used.
Type hints can help you even more here.
def remove_all_values(els: list, val) -> list:
Or
from typing import Any, List def remove_all_values(els: List[Any], val: Any) -> List[Any]:
With type hints you can tell the type of each argument right in the function's definition. In our example we say that the first element is a list with any type of arguments (List[Any]), the second argument can be of any type at all (val: Any), and the function returns the list of the same kind (List[Any])
You can also use generics in order to show that elements inside of input list have the same type as elements inside of output list
from typing import List, TypeVar Element = TypeVar('Element') def remove_all_values(els: List[Element], val: Element) -> List[Element]:
Cool, right? There are some official resources you might want to check out:
- PEP 484 - Type Hints - provides standard definitions and tools, along with some conventions for situations where the annotations are not available;
- PEP 483 - The Theory of Type Hints - basic concepts of type theory, gradual typing, some general rules, etc.;
- typing module documentation.
Well, it's high time to test how does it work. First, let's try to break the system and do the following after the function's definition:
print(remove_all_values(4, 3))
The result of running will be:
Traceback (most recent call last): ... inprint(remove_all_values(4, 3)) ... in remove_all_values els.remove(val) AttributeError: 'int' object has no attribute 'remove'
This is an exception, because instead of using list we are using int (4). But the important thing is that we will get the exact same exception even without using type hints. This means that no type checking happens during the runtime, even though you can get access to type hints by using attribute __annotation__
>>> remove_all_values.__annotations__ {'els': typing.List[typing.Any], 'val': typing.Any, 'return': typing.List[typing.Any]}
In order to make type checking you might want to use an extra tool - mypy, for example.
$ python3.5 -mmypy test.py ... error: Argument 1 to "remove_all_values" has incompatible type "int"; expected "List[Any]"
But not only mypy can help you to highlight the typing error like the one above. Some code editors have this feature as well. For example here is how PyCharm helps out.
(Let us know in comments how other Code Editors support type hints)
Type hints aren't something new and you can always use mypy in Python 2. In order to do so, you should install the typing module.
$ sudo pip2.7 install typing
Move your type hints into the comments.
from typing import Any, List def remove_all_values(els, val): # type: (List[Any], Any) -> List[Any]
Now when you use mypy you should add an extra parameter --py2
$ python -mmypy --py2 test.py
As you can see, this article briefly explains what type hints are. But information stated there is more than enough for you to start experimenting. Maybe you will decide to use it in your next project, as for Python there aren’t any specific recommendations on the subject, which means you can still write true python code without using type hints.
If you have any thoughts on the matter of pros and cons of using type hints, please, share them in comments.
PS: Here are some other examples of type hints, so you'd understand whether you want to use it in your code or not.
def notify_by_email(employees: Set[Employee], overrides: Mapping[str, str]) -> None AnyStr = TypeVar('AnyStr', str, bytes) def concat(x: AnyStr, y: AnyStr) -> AnyStr: def do_things(var1: Tuple, ff: Callable) -> None: def do_things(var1: Tuple[int, str], ff: Callable[[int, str], None]) -> None: def remove_all_values(els: Iterable[Any], val: Any) -> Iterable[Any]:
PPS: Since we only have Python 3 on CheckiO, we will start implementing initial code for our missions.
Welcome to CheckiO - games for coders where you can improve your codings skills.
The main idea behind these games is to give you the opportunity to learn by exchanging experience with the rest of the community. Every day we are trying to find interesting solutions for you to help you become a better coder.
Join the Game