Python 3: Variable Annotations

Python added syntax for what something called Variable Annotation in the version 3.6. Variable Annotation is basically an enhancement of type hinting, which was introduced in Python 3.5. The full explanation behind Variable Annotation is explained in PEP 526. In this article, we will have a quick refresher on type hinting and then introduce the new Variable Annotation syntax.

What’s Type Hinting?

Type hinting in Python is basically declaring that the parameters in your functions and methods have a certain type. Python does not enforce the type “hints”, but you can use tools like mypy to enforce the type hints in much the same way that C++ enforces type declarations at run time. Let’s look at a normal function with no type hints added:

def add(a, b):
    return a + b

if __name__ == '__main__':
    add(2, 4)

Here we create an add() function that takes two parameters. It adds the two parameters and returns the result. What we don’t know is what we need to pass to the function. We could pass it integers, floats, lists, or strings and it would most likely work. But would it work the way we expected it to? Let’s add some type hinting to our code:

def add(a: int, b: int) -> int:
    return a + b

if __name__ == '__main__':
    print(add(2, 4))

Here we change the definition of the function to utilize type hinting. You will notice that now the parameters are marked with what type they should be:

  • a: int
  • b: int

We also hint at the return value, which is what the “-> int” is. That means we expect an integer as our return value. If you try calling the add() function with a couple of strings or a float and an integer though, you won’t see an error. As I said, Python just allows you to hint at what the type of the parameters should be, but it does not enforce it.

Let’s update the code to the following:

def add(a: int, b: int) -> int:
    return a + b

if __name__ == '__main__':
    print(add(5.0, 4))

If you run this, you will see it executes just fine. Now let’s install mypy using pip:

pip install mypy

Now that we have mypy, we can use it to determine if we are using our function correctly. Open up a terminal and navigate to the folder that you save the above script to. Then execute the following command:

mypy hints.py

When I ran this command, I received the following output:

hints.py:5: error: Argument 1 to "add" has incompatible type "float"; expected "int"

As you can see, mypy found a problem with our code. We are passing in a float for the first argument instead of an int. You can use mypy on a continuous integration server that can check your code for these kinds of issues before committing pushing your submission to your branch or run it locally before committing your code at all.


Variable Annotation

Let’s say you want to not just annotate function parameters but also regular variables. In Python 3.5, you couldn’t do this using the same kind of syntax as you would for function parameters as it would raise a SyntaxError. Instead you would need to use comments, but now that 3.6 is out, we can use the new syntax! Let’s look at an example:

from typing import List

def odd_numbers(numbers: List) -> List:
    odd: List[int] = []
    for number in numbers:
        if number % 2:
            odd.append(number)

    return odd

if __name__ == '__main__':
    numbers = list(range(10))
    print(odd_numbers(numbers))

Here he specify that the variable, odd, should be a list of integers. If you run mypy against this script, you will receive no output as we are doing everything correctly. Let’s try changing the code to add something other than an integer though!

from typing import List

def odd_numbers(numbers: List) -> List:
    odd: List[int] = []
    for number in numbers:
        if number % 2:
            odd.append(number)

    odd.append('foo')

    return odd

if __name__ == '__main__':
    numbers = list(range(10))
    print(odd_numbers(numbers))

Here we add a new line that appends a string to the list of integers. Now if we run mypy against this version of the the code we should see the following:

hints2.py:9: error: Argument 1 to "append" of "list" has incompatible type "str"; expected "int"

Just to reiterate, in Python 3.5 you could do variable annotation, but you had to put that annotation in a comment:

# Python 3.6
odd: List[int] = []

# Python 3.5
odd = [] # type: List[int]

Note that if you change the code to use the Python 3.5 variation of variable annotation syntax, mypy will still flag the error correctly. You have to specify the “type:” after the pound sign though. If you remove that, then it’s no longer variable annotation. Basically all PEP 526 added was making the syntax more uniform across the language.


Wrapping Up

At this point you should have enough information to start doing variable annotation in your own code whether you’re using Python 3.5 or 3.6 I think it’s a neat concept and will be especially useful to those programmers that work with others that are more familiar with a static typed language.


Related Reading