How to use static type checking using Dict with different value types in Python 3.6?

ぃ、小莉子 提交于 2020-01-14 14:48:30

问题


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:

  1. 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.
  2. The keys must all be strings.
  3. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!