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
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With