13

I've been adding static typechecking to our python project, for example like this:

from typing import List
from something import MyOtherClass

class MyClass:
    def __init__(self) -> None:
        self.some_var = None  # type: List[MyOtherClass]

However, now the linters we use (flake8 and pylint) report for example List as unused variables, since they are not used in actual code. (pep8 handles it fine, by the way).

So we end up changing the code to this:

from typing import List  # noqa # pylint: disable=unused-import
from something import MyOtherClass  # noqa # pylint: disable=unused-import

class MyClass:
    def __init__(self) -> None:
        self.some_var = None  # type: List[MyOtherClass]

Is there any better solution to solve this? We don't want to disable all unused import warnings.

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
tobspr
  • 8,200
  • 5
  • 33
  • 46

2 Answers2

6

Python 3.6 implements PEP 526: Syntax for Variable Annotations, which as the name suggests introduces new syntax for variable annotations, removing the need for type comments.

In the new syntax, your code would be rewritten as:

from typing import List, Optional
from something import MyOtherClass

class MyClass:

    def __init__(self) -> None:
        self.some_var: Optional[List[MyOtherClass]] = None

... or alternatively:

from typing import List, Optional
from something import MyOtherClass

class MyClass:

    some_var: Optional[List[MyOtherClass]]

    def __init__(self) -> None:
        self.some_var = None

Since List and MyOtherClass now appear as actual tokens in the code, rather than comments, linters should have no trouble acknowledging that they are indeed being used.

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
  • I am new to type checking, so sorry if it is a stupid question, but is it really necessary to define that `__init__()` (the constructor) returns `None` ? – Guillaume Nov 24 '16 at 08:58
  • 4
    @Guillaume according to [PEP 484](https://www.python.org/dev/peps/pep-0484/#the-meaning-of-annotations), *"the return type of `__init__` ought to be annotated with `-> None` . The reason for this is subtle. If `__init__` assumed a return annotation of `-> None` , would that mean that an argument-less, un-annotated `__init__` method should still be type-checked? Rather than leaving this ambiguous or introducing an exception to the exception, we simply say that `__init__` ought to have a return annotation"* – Zero Piraeus Nov 24 '16 at 09:02
  • Ah great, looking forward to the 3.6 release then! – tobspr Nov 24 '16 at 10:20
4

@Zero Piraeus answer offers the most recent solution to this (i.e use variable annotations, also see: What are variable annotations in Python 3.6?).

Apart from that, you don't even need to import List when you're using # type: comments. mypy doesn't require them to be imported and neither to pyflakes or pylint as far as I am aware.

There's no need to import names from typing unless you require to use their name somewhere that Python actually performs a name look-up (and in comments, this isn't required.)

Community
  • 1
  • 1
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • I just tried it, and mypy does not require imports of the typing module members, but it requires imports of any other classes/types not in the typing module – tobspr Nov 24 '16 at 10:22
  • @tobspr yep it does. If you only use `type:` comments you don't need to import stuff from `typing`. Everything else is of course required as an import. if you define a function that takes a list as `def foo(a: List[int])` you'll need to import `List` from `typing` in order for the name to be resolved. – Dimitris Fasarakis Hilliard Nov 24 '16 at 10:24