Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mypy: How should I type a dict that has strings as keys and the values can be either strings or lists of strings?

I am using Python 3.8.1 and mypy 0.782. I don't understand why mypy complains about the following code:

from typing import Union, List, Dict
Mytype = Union[Dict[str, str], Dict[str, List[str]]]
s: Mytype = {"x": "y", "a": ["b"]}

Mypy gives the following error on line 3:

Incompatible types in assignment (expression has type "Dict[str, Sequence[str]]", variable has type "Union[Dict[str, str], Dict[str, List[str]]]")

If I change the last line to s: Mytype = {"a": ["b"]} mypy doesn't complain. However, when adding yet one more line s["a"].append("c") leads to an error:

error: Item "str" of "Union[str, List[str]]" has no attribute "append"

How can the above mentioned be explained? How should I type a dict that has strings as keys and the values can be either strings or lists of strings?

Found this: https://github.com/python/mypy/issues/2984#issuecomment-285716826 but still not completely sure why the above mentioned happens and how should I fix it.

EDIT: Although it's still not clear why the suggested modification Mytype = Dict[str, Union[str, List[str]]] does not resolve the error with s['a'].append('c') I think the TypeDict approach suggested in the comments as well as in https://stackoverflow.com/a/62862029/692695 is the way to go, so marking that approach as a solution.

See similar question at: Indicating multiple value in a Dict[] for type hints, suggested in the comments by Georgy.

like image 574
jjei Avatar asked Jul 12 '20 13:07

jjei


People also ask

Can we use string as key in dictionary?

Second, a dictionary key must be of a type that is immutable. For example, you can use an integer, float, string, or Boolean as a dictionary key.

Can a dictionary have a string as value?

Dictionary values can be just about anything (int, lists, functions, strings, etc). For example, the dictionary below, genderDict has ints as keys and strings as values.

Can a dict have a list as value?

It definitely can have a list and any object as value but the dictionary cannot have a list as key because the list is mutable data structure and keys cannot be mutable else of what use are they.

Should I use dict or {}?

dict is "easier to read" than {} and the meaning is clearer... especially for those who use multiple languages and find it difficult to remember whether it's braces, brackets, curly braces...


1 Answers

Because s: Mytype cannot have type Dict[str, str] and type Dict[str, List[str]] at the same time. You could do what you want like this:

Mytype = Dict[str, Union[str, List[str]]]

But maybe problems, because Dict is invariant


Also you could use TypedDict, but only a fixed set of string keys is expected:

from typing import List, TypedDict

MyType = TypedDict('MyType', {'x': str, 'a': List[str]})
s: MyType = {"x": "y", "a": ["b"]}

s['a'].append('c')

NOTE:

Unless you are on Python 3.8 or newer (where TypedDict is available in standard library typing module) you need to install typing_extensions using pip to use TypedDict


And, of course, you can use Any:

Mytype = Dict[str, Any]
like image 84
alex_noname Avatar answered Oct 22 '22 07:10

alex_noname