11

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.

jjei
  • 1,180
  • 3
  • 13
  • 21
  • 1
    Does this answer your question? [Indicating multiple value in a Dict\[\] for type hints](https://stackoverflow.com/questions/48054521/indicating-multiple-value-in-a-dict-for-type-hints) – Georgy Jul 12 '20 at 14:05
  • @georgy It touches the same area, but doesn't solve the issue. I am ok without typed dict, but can't wrap my head around why mypy is not happy with `s["a"].append("c")` – jjei Jul 12 '20 at 16:14
  • Not sure why it doesn't solve the issue. You can check [this gist on mypy playground](https://mypy-play.net/?mypy=latest&python=3.8&gist=9c95b8b27199f05deec422a867da742d) with the example of how you'd use the `TypedDict` here. It works fine. Or if you are interested specifically about the error with `s["a"].append("c")`, well, then it's because in case if `s['a']` is of type `str` then it doesn't have the `append` attribute. This is why you see the error. See also [Accessing an attribute of a variable typed as Union throws an error](https://stackoverflow.com/q/52493462/7851470). – Georgy Jul 12 '20 at 16:51
  • Using TypeDict solves the issue, but I didn't understand why it's needed. To me `Mytype = Dict[str, Union[str, List[str]]]` should be enough. Anyway, I think the TypeDict is actually way to go, because it's good to specify what type of values each key can have. – jjei Jul 12 '20 at 20:29

1 Answers1

14

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]
Neuron
  • 5,141
  • 5
  • 38
  • 59
alex_noname
  • 26,459
  • 5
  • 69
  • 86
  • With `Mytype = Dict[str, Union[str, List[str]]]` the line `s["a"].append("c")` still gives error: `Item "str" of "Union[str, List[str]]" has no attribute "append"` – jjei Jul 12 '20 at 15:43
  • Also, https://github.com/python/mypy/issues/7244 implies that you can actually use syntax like `Mytype = Union[Dict[str, str], Dict[str, List[str]]]` – jjei Jul 12 '20 at 15:51
  • 4
    @jjei `Union[Dict[str, str], Dict[str, List[str]]]` is not the same as `Dict[str, Union[str, List[str]]]`. In the first case, you can have only either dicts of str values or dicts with lists of strings as values, but not values of mixed types. – Georgy Jul 12 '20 at 16:42
  • Thanks for clarifying this. – jjei Jul 12 '20 at 20:29