Suppose I have a mapping that maps string -> int, and int -> string in one dictionary. Is there a way to express that with type annotations in a strict way? Currently I can do Dict[Union[str, int], Union[str, int]]
, but that allows for str -> str, and int -> int, which I do not actually want.
One might try Union[Dict[str, int], Dict[str, int]]
, but then only one of these is true for the scope it appears in.
Advertisement
Answer
This can be a solution. If you need, you can re-define other methods.
You can also do a d=cast(mydict,existing_dict)
.
JavaScript
x
36
36
1
from typing import overload
2
3
4
class mydict(dict):
5
6
@overload
7
def __getitem__(self, k: int) -> str:
8
9
@overload
10
def __getitem__(self, k: str) -> int:
11
12
def __getitem__(self, k):
13
return super(mydict, self).__getitem__(k)
14
15
@overload
16
def __setitem__(self, k: int, v: str) -> None:
17
18
@overload
19
def __setitem__(self, k: str, v: int) -> None:
20
21
def __setitem__(self, k, v):
22
super(mydict, self).__setitem__(k, v)
23
24
25
m = mydict()
26
m['a'] = 1
27
m[1] = 'a'
28
x1: str = m[1]
29
x2: int = m['a']
30
m['a'] = 1
31
m[1] = 'a'
32
x3: int = m[1] # mypy error
33
x4: str = m['a'] # mypy error
34
m[2] = 2 # mypy error
35
m['b'] = 'b' # mypy error
36
How @MisterMiyagi suggest, also a Protocol can work:
JavaScript
1
15
15
1
from typing import overload, Protocol
2
class mydictprotocol(Protocol):
3
4
@overload
5
def __getitem__(self, k: int) -> str:
6
7
@overload
8
def __getitem__(self, k: str) -> int:
9
10
@overload
11
def __setitem__(self, k: int, v: str) -> None:
12
13
@overload
14
def __setitem__(self, k: str, v: int) -> None:
15