Even a Feature That You Do Not Use Can Bite You

By Petr Zemek

Let’s have a look at a simple piece of Python code that I have both accidentally written and seen in code reviews which does an entirely different thing than expected.

Pop Quiz

What does the following code do when being run in Python 3.6 or newer?

 ages = {} ages['John']: 42 print(ages['John']) 

Options:

  1. Prints 42.
  2. Fails with SyntaxError on line 2.
  3. Fails with KeyError on line 3.

Correct Answer

The correct answer is number three.

Why?

The issue is on the following line:

 ages['John']: 42 

This statement doesn’t actually do anything (it is a no-op). What we have meant to write is this:

 ages['John'] = 42 

Hmm, fair enough. However, why doesn’t the original code raise SyntaxError? The reason is that it is actually valid Python code that uses variable annotations, which is a feature introduced in Python 3.6. In the previous versions of Python, the code would have raised SyntaxError.

In short, variable annotations are meant to allow programmers to provide so-called type hints. These hints can be then used by your IDE, documentation generator, or type checker, such as mypy. For example, the following line says that the type of variable x should be int:

 x: int 

Then, when you write

 x = 'Hey there!' 

a type checker may warn you that there is a type mismatch. As a side note, apart from variables, type hints may also be given when writing functions (function annotations):

 def transmogrify(v: [int]) -> int: ... 

Anyway, let’s get back to our original line:

 ages['John']: 42 

You may be wondering: “But the left-hand side is not a variable! And the right hand side is not a type!”. You are right, but that does not matter. According to the Annotating expressions section, the target of the annotation can be any valid assignment target. The section also gives the following example:

 d = {} d['b']: int # Annotates d['b'] with int. 

So, we can see that our original line

 ages['John']: 42 

says that we annotate ages['John'] with 42.

As for the fact that 42 is not a type, this also does not matter as the right-hand side of an annotation can be any valid Python expression, so you can even write

 x: print(1 + 1) 

This will print 2 when evaluating the code and annotate x with None (the result of print()).

Discussion

Apart from comments below, you can also discuss this post at /r/Python.