Python 3.5 added an interesting new library called typing. This adds type hinting to Python. Type hinting is kind of declaring your functions arguments to have a certain type. However the type hinting is not binding. It’s just a hint, so there’s nothing preventing the programmer from passing something they shouldn’t. This is Python after all. You can read the type hinting specification in PEP 484 or you can just read the theory behind it in PEP 483.
Let’s take a look at a simple example:
>>> def some_function(number: int, name: str) -> None: print("%s entered %s" % (name, number)) >>> some_function(13, 'Mike') Mike entered 13
This means that some_function expects two arguments where the first is an integer and the second is a string. It should also be noted that we have hinted that this function returns None.
Let’s back up a bit and write a function the normal way. Then we’ll add type hinting to it. In the following example, we have a function that takes list and a name, which in this case would be a string. All it does is check if the name is in the list and returns an appropriate Boolean.
def process_data(my_list, name): if name in my_list: return True else: return False if __name__ == '__main__': my_list = ['Mike', 'Nick', 'Toby'] print( process_data(my_list, 'Mike') ) print( process_data(my_list, 'John') )
Now let’s add type hinting to this function:
def process_data(my_list: list, name: str) -> bool: return name in my_list if __name__ == '__main__': my_list = ['Mike', 'Nick', 'Toby'] print( process_data(my_list, 'Mike') ) print( process_data(my_list, 'John') )
In this code we hint that the first argument is a list, the second is a string and the return value is a Boolean.
According to PEP 484, “Type hints may be built-in classes (including those defined in standard library or third-party extension modules), abstract base classes, types available in the types module, and user-defined classes”. So that means we can create our own class and add a hint.
class Fruit: def __init__(self, name, color): self.name = name self.color = color def salad(fruit_one: Fruit, fruit_two: Fruit) -> list: print(fruit_one.name) print(fruit_two.name) return [fruit_one, fruit_two] if __name__ == '__main__': f = Fruit('orange', 'orange') f2 = Fruit('apple', 'red') salad(f, f2)
Here we create a simple class and then a function that expects two instances of that class and returns a list object. The other topic that I thought was interesting is that you can create an Alias. Here’s a super simple example:
Animal = str def zoo(animal: Animal, number: int) -> None: print("The zoo has %s %s" % (number, animal)) if __name__ == '__main__': zoo('Zebras', 10)
As you may have guessed, we just aliased the string type with the variable Animal. Then we added a hint to our function using the Animal alias.
Wrapping Up
When I first heard about type hinting, I was intrigued. It’s a neat concept and I can definitely see uses for it. The first use case that springs to my mind is just self-documenting your code. I’ve worked on too many code bases where it’s difficult to tell what a function or class accepts and while type hinting doesn’t enforce anything, it would certainly bring some clarity to some of that ambiguous code. It would be neat if some Python IDEs added an optional flag that could check your code’s type hints and make sure you’re calling your code correctly too.
I highly recommend checking out the official documentation as there’s a lot more information there. The PEPs also contain a lot of good details. Have fun and happy coding!
Related Reading
- Official documentation on the typing module
- PEP 0484 — Type Hints
I’ve created some tools to take advantage of type hints for both type checking and documentation: https://pypi.python.org/pypi/typeguard and https://pypi.python.org/pypi/sphinx-autodoc-typehints
Also, in case you didn’t know, PyCharm already supports type hints and uses them for autocompletion and static type checking.
I actually saw that PyCharm understood type hinting, but I didn’t realize it did type checking with it.
It’s by no means perfect yet but it’s a good start.
I think there is a typo in the hinted `process_data` function: the firm should be `def process_data(my_list: list, name: str) -> bool:`
You are correct. I got ahead of myself and somehow messed that example up. It’s fixed now. Thanks!
Pingback: Python 3: Variable Annotations - The Mouse Vs. The Python