Mypy error on dict of dict: Value of type "object" is not indexable
Solution 1
Static typing is tricky. mypy
can determine that the values of dictionary
don't all have the same type, but that's as far as it goes. The static type of dictionary
is Dict[str,object]
, based on the initial value. However, mypy
doesn't try to simulate the code further, which means that it has no idea if d['sub_dict']
is still another dict
at the point where you try to index it with key2
, which leads to the type error.
One thing you can do is help mypy
by telling it that a particular value can be treated as having a specific type, using typing.cast
.
print(typing.cast(typing.Dict[str,dict], d['sub_dict'])['key2'])
At runtime, typing.cast
is effectively an identity function; it just returns its second argument. mypy
treats it like a stronger type hint, saying that regardless of any previous hints or annotations, d['sub_dict']
should be treated as a Dict[str,dict]
.
Note, though, that by using cast
, you are telling mypy
that you are assuming responsibility for assuring that dictionary['sub_dict']
is, in fact, a dict
at runtime, since this isn't something that you can convey with a static type. You might think that something like
dictionary : Dict[str,Union[int,dict]] = ...
would work, but that's just telling mypy
that it would be a type error to write dictionary['foo'] = 'bar'
, since 'bar'
is neither an int
or a dict
. Even with the more accurate type hint, there's still no way for mypy
to know what type of value dictionary
maps any specific key to.
You could use Any
as well:
dictionary: Dict[str,Any] = ...
because now you are saying that any type can be used as a value, and that any type can be assumed for the result of indexing, and the two types don't have to line up. That is, dictionary['key1'] = 3
is fine because int
is compatible with Any
, but dictionary['sub_dict']['key2']
is also fine because whatever dictionary['sub_dict']
produces is also compatible with Any
, and you can assume that that type is itself indexible. In effect, it covers any use of dictionary
anywhere in your code, instead of a specific location where you used cast
to make assertions about what should be allowed.
Major digression: there is a notion of dependent types, the simplest example of which is a type like PositiveInt
which would be identical to int
except it doesn't permit negative values. dictionary
would seem to have a similar dependent type, where the type of the values is really a function of the actual data stored in a value. For example, imagine if you could use an instance of dict
with Dict
to specify the type of its values.
dictionary: Dict[str, {"key1": int, "sub_dict": dict}] = {'key1': 1,
'sub_dict': {'key2': 0}
}
Now, not only could mypy
tell that dictionary['key1']
is supposed to be an int
, but that dictionary
itself can never have any keys other than key1
and sub_dict
. (And in this hypothetical world, a defaultdict
could map an arbitrary unspecified key to a default type.)
Solution 2
A simple change that worked for me is this.
from typing import Any, Dict
dictionary: Dict[str, Any] = {
'key1': 1,
'sub_dict': {'key2': 0},
}
This tells mypy that the values of your dictionaries can be of any type. If your values are of different variety (integers, floats, strings, dict etc) then you are better off using the Any
type annotation.
For OP's dictionary specifically, the below might be more appropriate.
from typing import Tuple, Dict
dictionary: Dict[str, Tuple[int, Dict]] = {
'key1': 1,
'sub_dict': {'key2': 0},
}
![Pierre S.](https://i.stack.imgur.com/c2FfO.jpg?s=256&g=1)
Pierre S.
C++, Python programmer specialized in computer vision and data science.
Updated on July 15, 2022Comments
-
Pierre S. almost 2 years
I have the following dictionary on python:
dictionary = { 'key1': 1, 'sub_dict': {'key2': 0}, }
When I run mypy on the following line:
print(dictionary['sub_dict']['key2'])
it raises the error
Value of type "object" is not indexable
-
Pierre S. over 5 yearsIsn't it dangerous to cast
d['sub_dict']
astyping.Dict[str,dict]
since my first keykey1
is actually an integer ? -
chepner over 5 yearsI was just going to add a note about that. In some sense,
dictionary
simply isn't well-typed;dictionary['key1']
anddictionary['sub_dict']
have different types. When you usecast
, you are tellingmypy
that you are taking responsibility to ensure that the cast will remain valid at runtime. -
Pierre S. over 5 years
dictionary : Dict[str,Any] = ...
also allows to remove the error -
chepner over 5 yearsAh, OK, I wasn't appreciating exactly what
Any
meant.dictionary['key1'] = 3
is fine becauseint
andAny
are compatible, anddictionary['sub_dict']['key2']
is fine becausedict
andAny
are compatible.