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.