问题
Trying to use static types in Python code, so mypy
can help me with some hidden errors. It's quite simple to use with single variables
real_hour: int = lower_hour + hour_iterator
Harder to use it with lists and dictionaries, need to import additional typing
library:
from typing import Dict, List
hour_dict: Dict[str, str] = {"test_key": "test_value"}
But the main problem - how to use it with Dicts with different value types, like:
hour_dict = {"test_key": "test_value", "test_keywords": ["test_1","test_2"]}
If I don't use static typing for such dictionaries - mypy shows me errors, like:
len(hour_dict['test_keywords'])
- Argument 1 to "len" has incompatible type
So, my question: how to add static types to such dictionaries? :)
回答1:
You need a Union
type, of some sort.
from typing import Dict, List, Union
# simple str values
hour_dict: Dict[str, str] = {"test_key": "test_value"}
# more complex values
hour_dict1: Dict[str, Union[str, List[str]]] = {
"test_key": "test_value",
"test_keywords": ["test_1","test_2"]
}
In general, when you need an "either this or that," you need a Union
. In this case, your options are str
and List[str]
.
There are several ways to play this out. You might, for example, want to define type names to simplify inline types.
OneOrManyStrings = Union[str, List[str]]
hour_dict2: Dict[str, OneOrManyStrings] = {
"test_key": "test_value",
"test_keywords": ["test_1","test_2"]
}
I might also advise for simplicity, parallelism, and regularity to make all your dict
values pure List[str]
even if there's only one item. This would allow you to always take the len()
of a value, without prior type checking or guard conditions. But those points are nits and tweaks.
回答2:
While using Union
is indeed one way of doing it, a more precise solution would be to use the (currently experimental) TypedDict type, which lets you assign specific types per each string key.
In order to use this type, you must first install the mypy_extensions
3rd party library using pip. You can then do the following:
from typing import List
from mypy_extensions import TypedDict
MyDictType = TypedDict('MyDictType', {
'test_key': str,
'test_keywords': List[str],
})
hour_dict: MyDictType = {
"test_key": "test_value",
"test_keywords": ["test_1","test_2"]
}
Note that we need to explicitly denote hour_dict
of being of type MyDictType
. A slightly cleaner way of doing this is to use MyDictType
as a constructor -- at runtime, MyDictType(...)
is exactly equivalent to doing dict(...)
, which means the below code behaves exactly identically to the above:
hour_dict = MyDictType(
test_key="test_value",
test_keywords=["test_1","test_2"]
)
Finally, note that there are a few limitations to using TypedDict:
- It's useful only when the dict will contain specific keys with types that are all known at compile time -- you should use regular
Dict[...]
if you expect a truly dynamic dict. - The keys must all be strings.
- At time being, this type is understood only by mypy (though I understand there are plans to eventually add TypedDict to PEP 484 once it's a little more battle-tested, which would mean any PEP 484 compliant typechecker would be required to support it).
(TypedDict
was designed to make it easier to work with JSON blobs/dicts when writing serialization/deserialization logic, which is the reason for these constraints.)
来源:https://stackoverflow.com/questions/48013561/how-to-use-static-type-checking-using-dict-with-different-value-types-in-python