Reputation: 2972
The kind of data structure I parse in my Python script is a json file which, after json.load(file_handle)
, is of type <class 'dict'>
. So far so good. Now for a function using it as an input argument, I want a type hint for the parsed json. I read in the typing documentation, that for dict
s as arguments, I should use Mapping[key_type, value_type]
:
from typing import Mapping
def foo(json_data: Mapping[str, str]) -> None:
...
The json I parse has str
-type keys and str
-type values, but more often than not, its structure is highly recursive. Hence a value is more likely to be a dict
with str
keys and even such dict
s as values. It is very nested, until, at the deepest level, the last dict finally has str
keys and str
values.
So how do I represent this data structure more precisely? I was thinking something, along the lines of this question, that it might be:
Union[Mapping[str, str], Mapping[str, Mapping]]
But it does seem to represent only one level of recursion. Is there a better way to type-hint this?
Upvotes: 23
Views: 22495
Reputation: 54193
Nowadays, mypy and other type checkers support recursive definitions. A (somewhat) complete example of a Json type is:
import typing as t
JsonType: t.TypeAlias = t.List['JsonValue'] | t.Mapping[str, 'JsonValue']
JsonValue: t.TypeAlias = str | int | float | None | JsonType
def foo(json_data: JsonType) -> None:
"""Your implementation here"""
This should type check.
Upvotes: 11
Reputation: 65
I was trying to comment a reply in the above thread but it won't let me without enough reputation (but will let me post an answer).
I would follow along with the answer vivax provided and then add the following for unknown data:
from typing import Mapping, Any
Mapping[str, Any] # or Mapping[Any, Any] if the keys are also unknown
You could also have more granularity by defining your own JSON type
Upvotes: 0
Reputation: 486
you can simply use Mapping[K, V]
as the value for another Mapping
. let's say your json looks like this:
{
"person_1": {"money": 1000},
"person_2": {"money": 1000}
}
in this case you can use a type hint that looks like this:
Mapping[str, Mapping[str, int]]
but let's say you have something more complex, like this:
{
"person_1": {
"money": 1000,
"job": "coder",
}
}
how could you type hint this then? you can use Union
(from typing) to do something like this:
Mapping[str, Mapping[str, Union[str, int]]]
but now we would run into a problem with tools like mypy thinking that our_dict["person_1"]["job"]
has the type Union[str, int]
well it is not wrong, that is what we told it after all. something that could help here is TypedDict from typing_extensions.
from typing_extensions import TypedDict
class Person(TypedDict):
money: int
job: str
# type hint you would use in your function:
Mapping[str, Person]
NOTE: in python 3.8 TypedDict
is part of typing
Upvotes: 16