Python type hints and typing support in examples

First article in this series about typing sup­port in Python will show you how to uti­li­ze type hints in this otherwi­se dyna­mic lan­gu­age. Both Python 2 and 3 will be cove­red. You will also see why you may need a type sup­port in the first pla­ce.

Python is based on duck-typing. But if your inter­nal method works just with strings, the code will be more self-defensi­ve, if it will accept only strings and not any­thing that can be poten­ti­ally (and incorrect­ly) con­ver­ted to a string. Swap­ped argu­ments is a good exam­ple.

Type hints

Type hints are use­ful for API/public methods, because you are let­ting the world know what type you expect (if you do expect a spe­ci­fic type). Addi­ti­o­nally, the­re are tools that can check pro­per use of types and dis­play war­nings befo­re a cus­to­mer dis­co­vers the­ir impro­per use in form of bugs in pro­ducti­on. The­re are dif­fe­rent appro­a­ches, so let’s start with the most com­pa­ti­ble and least rest­ricti­ve one.

Documentation strings

This appro­ach is the most com­pa­ti­ble one, because it does not rely on any lan­gu­age syn­tax and works in all ver­si­ons of Python. Unfor­tu­na­te­ly, that also means it can­not be used by type chec­king pro­grams, such as MyPy (descri­bed later).

Docu­men­tati­on strings start and end with either tri­ple quo­tes  ”„Docstring””” or tri­ple apo­stro­phes ”’Docstring”’. The­re are dif­fe­rent for­mats of docstrings: Epy­text, reStructu­red­Text, Num­Py, or Goo­gle. If you’re using PyCharm, you can set it in Tools/Python Inte­gra­ted Tools under Docstring for­mat. Sin­ce reStructu­red­Text is the default and also the most com­mon one, let’s use it in the following exam­ple. The docstring appro­ach is also called a Lega­cy Type Syn­tax.

A com­ple­te gui­de on how to use the Lega­cy Type Syn­tax is is avai­la­ble here.
I also cre­a­ted for you a han­dy, prin­table che­at she­et on reStructu­red­Text.

In the exam­ple abo­ve, cakes come in dif­fe­rent sizes and fla­vors. Humans with dif­fe­rent names and they can like either a Cake or a Human. Addi­ti­o­nally, we can bake() Cakes. At the end, we make a use of these clas­ses.

Can you spot all errors within seconds? I defi­ni­te­ly can­not. Luc­ki­ly, PyCharm’s code inspecti­on comes to rescue. If you spe­ci­fy a type in a docstring, it will make use of it. This is how the result looks like:

Python typing example using docstrings

The mis­ta­kes are now obvi­ous. Cake con­struc­tor takes a size argu­ment of type int, not str. We acci­den­tally swap­ped argu­ments in wrong_cake and me. Then we tried to bake() a sin­gle apricot_cake, when we can bake only list of cakes. And we obvi­ous­ly can­not bake dad. That’s insa­ne. What PyCharm did not find is try­ing to use „my cake” as one of the cakes, when we accept only Cake instan­ces.

Neverthe­less, the bene­fits are clear now – you can dis­co­ver bug imme­di­a­te­ly and you pro­vi­de a clear inter­fa­ce to others. No one has to guess what a human can like or what you can bake. To take any guesswork out of the equati­on, you can also dis­play the docu­men­tati­on in PyCharm by pres­sing Ctrl+Q or from menu View – Quick Docu­men­tati­on:

PyCharm Quick Documentation

Typing module

Modu­le typing is based on PEP 484 and adds types sup­port into Python. It is avai­la­ble via pypi and from Python 3.5 it is avai­la­ble as stan­dard lib­ra­ry. Python docu­men­tati­on descri­bes use of the typing modu­le qui­te well, so let’s have a look at con­cre­te exam­ples.

Types definition in comments

If you’d like to use the typing modu­le pri­or 3.5, you have defi­ne types in spe­cial com­ments star­ting with # type:. See the following exam­ple:

For method argu­ments, it is possi­ble to defi­ne type for each argu­ment indi­vi­du­ally. This cre­a­tes rather long and ugly code. Ano­ther appro­ach it to defi­ne types for the who­le method at once. This defi­ni­ti­on is pla­ced between method sig­na­tu­re and docstring (or method’s body if the­re is no docstring). Note that self does not requi­re type defi­ni­ti­on.

The com­ment appro­ach has no pro­blem with for­ward or cir­cu­lar refe­ren­ces. An exam­ple of a for­ward refe­ren­ce is defi­ni­ti­on of type of the likes argu­ment in the __init__() method of class Human. One of the possi­ble valu­es is Human, which is refe­ren­ce to the class itself befo­re it is defi­ned.

Type annotations

In Python 3.5 was intro­du­ced new syn­tax for type anno­tati­ons, based on PEP 526. Com­pa­ti­bi­li­ty with older Python ver­si­ons is achie­ved by the use of stub file with .pyi extensi­on (they are igno­re on older ver­si­ons). These files are sup­por­ted by PyCharm as well. Let’s have a look at con­cre­te exam­ple:

Types are now part of methods sig­na­tu­re, so it takes only few argu­ments or com­plex types and it no lon­ger fits on a sin­gle line. For this rea­son, it is a good idea to spe­ci­fy com­plex types befo­re­hand (such as the Human­Li­ke type). Ano­ther advan­tage of defi­ning types in advan­ce is the­ir reu­sa­bi­li­ty. Note that this can be done pri­or Python 3.5 as well.

Type anno­tati­ons are lan­gu­age syn­tax and not just com­ments. The­re­fo­re, can run into pro­blems with for­ward or cir­cu­lar refe­ren­ces as men­ti­o­ned in pre­vi­ous secti­on. The­re are seve­ral ways of sol­ving this. The easiest and shor­test is spe­ci­fy­ing the type as a string: Uni­on[Cake, ‘Human’] . The down­si­de of this appro­ach is that PyCharm will not use this type in type chec­king.

Conclusion

Adding type hints to your pro­ject can impro­ve the code qua­li­ty and allow you to catch bugs more quick­ly. This is most pro­mi­nent when you use some IDE that high­li­ghts impro­per types usage, such as PyCharm. If you don’t need to sup­port older Python ver­si­ons, you can use type anno­tati­ons, which don’t even add that much code and are easy to use.

Make sure you check out the other articles in this series.

Zanechte komentář / Leave a comment

Your email address will not be published. Required fields are marked *