1

I have such define in my python code:

options = defaultdict(lambda: defaultdict(lambda: defaultdict(str)))
options['modem1']['ByCost'] = ...
options['modem1']['ByCarrier'] = ...
options['modem1']['BySimSignal'] = ...
options['modem1']['SwitchBackByTimer'] = ...

And it fails during my pre-commit/mypy checking, for the message that :

Need type annotation for 'options'

Since I don't see anything wrong with this code, so is there any work around that I can have it passed the mypy checking for this line ?

Btw, I am looking for alternative ways, other than setup this in my code :

disable_error_codes with error code var-annotated

Thanks a lot !

Jack

anthony sottile
  • 61,815
  • 15
  • 148
  • 207
user3595231
  • 711
  • 12
  • 29

1 Answers1

4

On Python 3.9+, thanks to PEP 585, you can use defaultdict itself for type annotations, making this simple, if somewhat redundant/verbose (see bottom of answer for 3.9+-only solution that's a little less redundant/verbose, but can't be backported to 3.8 and earlier as easily):

from collections import defaultdict

options: defaultdict[str, defaultdict[str, defaultdict[str, str]]] = defaultdict(lambda: defaultdict(lambda: defaultdict(str)))

Note that this assumes you actually have a three level defaultdict; your code is only going down two levels, so you'll get a checking error if you do:

options['modem1']['ByCost'] = 'abc'

because you really needed one more level of str keys:

options['modem1']['ByCost']['xyz'] = 'abc'

If you actually wanted two layers of defaultdict, trim it back to just:

options: defaultdict[str, defaultdict[str,str]] = defaultdict(lambda: defaultdict(str))

options['modem1']['ByCost'] = 'abc'

If you're on Python 3.8 or earlier, replace the annotation using defaultdict with the typing.DefaultDict annotation, making it:

from collections import defaultdict
from typing import DefaultDict

options: DefaultDict[str, DefaultDict[str, DefaultDict[str, str]]] = defaultdict(lambda: defaultdict(lambda: defaultdict(str)))
# Or if only two levels:
options: DefaultDict[str, DefaultDict[str, str]] = defaultdict(lambda: defaultdict(str))

Less redundant solution

Note: The 3.9 usage allows you to both annotate and use the annotated type, so as a 3.9+ only solution, you could do (showing two layer case for simplicity):

options = defaultdict[str, defaultdict[str,str]](lambda: defaultdict(str))

and not need to explicitly provide the annotation on options itself, as the top-level defaultdict is already annotated, implicitly annotating the variable it's assigned to.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271