DEV Community

Cover image for A Failed Experiment with Python Type Annotations

A Failed Experiment with Python Type Annotations

edA‑qa mort‑ora‑y on June 11, 2019

I like Python, but wish it had static typing. The added safety would go a long way to improving quality and reducing development time. So today I t...
Collapse
 
evanoman profile image
Evan Oman • Edited

Adding on to @victorosilva 's comment, the annotation import is only available on Python 3.7+. For Python 3.6 and below, the workaround to the self-referential issue is to put the class name in single quotes, as discussed here:

class Position:
    ...
    def __add__(self, other: 'Position') -> 'Position':
       ...
Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

Oh, I guess I'll try this approach first, since Ubuntu is on Python 3.6.8.

Collapse
 
evanoman profile image
Evan Oman

I also highly recommend conda environments, it is really nice to have per-project Python version and dependency specifications without messing with system Python.

Thread Thread
 
miniscruff profile image
miniscruff

Python has venv so you can just run python -m venv {path}.

Collapse
 
victorosilva profile image
Victor Silva • Edited

I'm not familiar with mypy, but the self-referential problem can be solved with PEP 563's

from __future__ import annotations

, which won't be needed in Python 4.

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

I will check this out, since it'd shift the balance in the favour of sticking with Python (my head is running towards C++, perhaps foolishly).

Collapse
 
dmerejkowsky profile image
Dimitri Merejkowsky

Thanks for the detailed article and the nice examples - it allows for an interesting discussion.

Here's my take on the subject (disclaimer: I'm using mypy in all my "serious" projects for a little bit than a year now)

First, maybe you missed the fact that mypy has a reveal_type function precisely so that you don't have to remember and or guess all the types.


sequence = [1, 2, 3]

def get_iter():
    res = iter(sequence)
    reveal_type(res)
    return res
$ mypy --strict foo.py
foo.py:26: error: Function is missing a type annotation
foo.py:28: error: Revealed type is 'typing.Iterator[builtins.int*]'

I know, this is a bit awkward to use, but it's there if you need it.

Second, I'm not quite sure why mypy does not infer return types - maybe it's a bug, maybe it's a design decision. The question came up for Rust by the way and I think the rationale may apply to Python too.

Finally, if you're still wondering whether to give mypy a go, I have compiled a real-world list of changes caused by the transition to static typing in this rather long blog post. You may find it interesting :)

Cheers!

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

Thank you.

I'm going to give it another go soon. The issue about self-referential types is essentially fixed in an upcoming Python release, and I can import from future now.

The return types are still annoying, but the reveal types will help. I got another comment that mypy may add an option to allow inferred return types.

At the moment, doing a refactoring, I'm leaning towards typing all the return types is the lesser of evils -- when compared to hunting down type mismatches.

Collapse
 
bosepchuk profile image
Blaine Osepchuk • Edited

Thanks for sharing this.

I've been meaning to experiment with python type annotations for a while but, based on the pain you're experiencing, I'll stay away for a while longer.

Collapse
 
moritzweber profile image
Moritz Weber

I haven't used it yet, but MonkeyType could help with that repetitive return value typing.