ProgrammingPython

How to Make Python Statically Typed – The Essential Guide

Python is a dynamically typed language – I’m sure you know that. This makes it easy and fun for beginners, as there’s no need to think about data types. Still, static typing has some benefits. Today we’ll explore how to make Python as statically typed as possible. 

Don’t feel like reading? Check out a video on the topic:

So, what’s the deal with dynamically typed languages? In a nutshell, it means there’s no code compilation, so the Python interpreter performs type checking as code runs. As a result, variable types are allowed to change throughout the application.  

It may sound like an advantage, but it can lead to strange and hard-to-track errors as the code base gets larger. 

On the other hand, statically typed languages perform type checks upon compilation (think C or Java). Further, they give you some feeling of safety, as you can immediately tell the type of parameter going into each function.  

Just a quick disclaimer before we start – Python will always be a dynamically typed language. There’s nothing you can do to make it static as Java or C. PEP 484 introduced type hints, so we’ll have to work with that. 

The article is structured as follows:

Type hints

Type hints are just that – hints. They show you which data type is expected, but nothing is stopping you from ignoring them. We’ll later explore how to force type checking by the Python interpreter, but let’s cover the basics first. 

Let’s make an example without type hints. Below is a function designed to add two numbers and return the sum. Python being Python, we don’t have to specify data types for parameters nor for the return value: 

The printed results from the function calls are 15, 15.3, and BobMark, respectively. To introduce the type hints, we have to do the following: 

  • For parameters – place the colon sign (:) right after the parameter name and specify the data type after 
  • For return value – place the arrow sign and the data type right after the parenthesis 

Here’s how to add type hints to our sum_numbers function: 

The function is a bit clearer to look at now, but we can still pass any data types – indicating that hints are just hints, not an obligation. 

Here are type hint recommendations, according to PEP 8: 

  • Use normal rules for colons, that is, no space before and one space after a colon 
  • Use spaces around the = sign when combining an argument annotation with a default value 
  • Use spaces around the -> arrow 

We’ll see how to force type checks later. 

Variable annotations

We can also provide type hints for variables. Once again, these are just hints and do not affect how the code runs. 

Here’s a quick example of how to add type hints to variables: 

Nothing is stopping you from assigning a value of 32.95 to the variable age, or assigning no to is_premium. Hints only indicate that you shouldn’t.  

Denoting more advanced data types

Everything we covered thus far is great, but what if you want to declare a list of strings or a dictionary where both keys and values are strings?  

That’s where the typing module comes into play. With it, you can use any more complex data types, such as lists, dictionaries, and tuples. You can also declare your own data types, but that’s a bit out of the scope for today. 

Let’s now use the typing module to declare a list of names, and then a dictionary of emails, where each email belongs to a single name. In all cases, the data type is a string:

A lot more verbose, but feels safer. Further, let’s see how to make a function parameter to be a list of integers. It’s pretty straightforward:
 
You now know how to use type hints to declare a variable of any data type, but how to force type checks on runtime? Let’s explore that next. 

Forcing type checking on runtime

I’m not aware of any methods of forcing type checks in the Notebook environment – so we’ll switch to scripts. I’ve made a file called static_typing.py, but feel free to name yours as you wish. 

Before proceeding, we’ll have to install a library called mypy. It is used to force type checks on runtime, just what we need. To install it, execute pip install mypy from the Terminal. 

We are now good to go. The static_typing.py script contains the following code:

From the Terminal, execute the script in the traditional way – by writing python static_typing.py.  
Execution without mypy

Image 1 – Execution without mypy (image by author)

The code will run without throwing an error. This is equivalent to what we did earlier in the Notebook. But we don’t want that. With mypy, we can force the type checks. From the Terminal, execute the following: mypy static_typing.py: 
Execution with mypy

Image 1 – Execution with mypy (image by author)

As you can see, the program crashed. Just the behavior we wanted, as the function expects two integers, but we didn’t provide that data types in the function calls. 

And that’s all you should know for starting out. Let’s wrap things up in the next section. 

Parting words

Let’s end this article with a couple of pros and cons of using type hints. 

Pros: 

  • They help to document your code – not a replacement for docstrings though 
  • They help in maintaining a cleaner codebase – for obvious reasons 
  • They improve the linting capabilities of your code editor – for example, PyCharm will show a warning if a type mismatch is detected 

Cons: 

  • Can be tedious to add – especially if you are not accustomed to them 

To conclude – type hints are not required but are good practice, especially when working on large codebases – even if you decide not to use mypy. 

What are your thoughts on type hints? Do you always use them, or just when the codebase gets larger? Let me know in the comment section below. 

Join my private email list for more helpful insights. 

Dario Radečić
Data scientist, blogger, and enthusiast. Passionate about deep learning, computer vision, and data-driven decision making.

You may also like

Leave a reply

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

More in Programming