I tried to instantiate an empty dictionary on the second level of an existing dict, then assign a key-value pair to it, but MyPy throws an error.
Here is a minimal example, which will reproduce it when MyPy checking is activated:
result = {"Test": "something"}
result['key'] = {}
result['key']['sub_key'] = ["some string", "another string"]
The error here will be something like:
mypy(error): Incompatible types in assignment (expression has type
"Dict[<nothing>, <nothing>]", target has type "List[str]")
How do I prevent this error? According to a similar problem, it was suggested to do
result['key'] = {} # type: ignore
as a workaround, but this does not seem very elegant, which is why I'm wondering if there is more one can do.
Right, so let's look at the first two lines here.
First, you define your dictionary result
. You define it like so:
result = {"Test": "something"}
You don't declare what types you expect the keys and values of result
to have, so MyPy is left to work it out for itself. Alright, it says, I can do this — you've got only strings as dictionary keys, and only strings as dictionary values, therefore result
must be of type dict[str, str]
.
Then we get to line 2:
result['key'] = {}
And MyPy, quite reasonably, raises an error. "Hey, this looks like a mistake!" it says. You've only got strings as dictionary values in this so far, and you haven't explicitly told MyPy that it's okay to have non-str
values in this dictionary, so MyPy reckons you've probably made an error here, and didn't mean to add that value to the dictionary.
No real need to look at the third line, because it's basically the same thing going on
There are a couple of ways of fixing this. In order of most-preferred (use if possible) to least-preferred (use only as a last resort):
You can tell MyPy that this specific dictionary has certain types associated with certain string-keys by annotating it as a TypedDict
. (You'll have to change your "sub-key"
key to "sub_key"
, however, as "sub-key" isn't a valid variable name.)
from typing import TypedDict
class KeyDict(TypedDict, total=False):
sub_key: list[str]
class ResultDict(TypedDict, total=False):
Test: str
key: KeyDict
result: ResultDict = {"Test": "something"}
result['key'] = {}
result['key']['sub_key'] = ["some string", "another string"]
You can tell MyPy that any value in this dictionary could be of type str
, or it could be of type dict[str, list[str]]
:
from typing import Union
result: dict[str, Union[str, dict[str, list[str]]]] = {"Test": "something"}
d: dict[str, list[str]] = {}
d['sub_key'] = ["some string", "another string"]
result['key'] = d
You can tell MyPy that the value in this dictionary could be anything (not that different from switching off the type-checker):
from typing import Any
result: dict[str, Any] = {"Test": "something"}
result['key'] = {}
result['key']['sub_key'] = ["some string", "another string"]
You can switch off the type-checker:
result = {"Test": "something"}
result['key'] = {} # type: ignore[assignment]
result['key']['sub_key'] = ["some string", "another string"] # type: ignore[index]
Anyway, it's a bit difficult to know what the "best" solution is here without knowing more about your use case.