如何向mypy说明领域特定约束?代码场景下的类型注解求解
I get it, dealing with mypy's strict type checking when you have domain knowledge it can't infer is super frustrating. Let's start with your code and the issue at hand:
from typing import Dict def a(my_dict: Dict[str,int])->int: return my_dict.get('a') or my_dict.get('b')
Mypy throws that Incompatible return value type error because dict.get() returns an Optional[int] (it can return None if the key doesn't exist), and it has no way of knowing your guarantee that either 'a' or 'b' is always present in the dictionary.
Since you don't want to modify the actual runtime logic of your code, here are two clean ways to fix this with just type annotations:
1. Use typing.cast to Explicitly Assert the Return Type
The simplest approach is to use cast to tell mypy that the result of your expression is definitely an int, not an Optional[int]. This doesn't change how your code runs at all—it's purely a hint for the type checker:
from typing import Dict, cast def a(my_dict: Dict[str,int])->int: return cast(int, my_dict.get('a') or my_dict.get('b'))
This works because you know from your domain logic that the or will always resolve to an int (since one of the keys must exist), so the cast is completely safe. Mypy will accept this without any complaints.
2. Define a Custom Union Type with TypedDict (More Strict Input Validation)
If you want to enforce at the parameter level that the input dictionary must have either 'a' or 'b' (instead of just trusting the return value cast), you can create a union of TypedDict types. This makes your type constraints more explicit and catches invalid inputs earlier in the process:
from typing import TypedDict, Union # Define two TypedDicts: one requiring 'a', one requiring 'b' class DictWithA(TypedDict): a: int class DictWithB(TypedDict): b: int # The input must match one of the two TypedDict types MyRequiredDict = Union[DictWithA, DictWithB] def a(my_dict: MyRequiredDict) -> int: return my_dict.get('a') or my_dict.get('b')
Now mypy will validate that any dictionary passed to a() has either 'a' or 'b' (and it can still have other extra keys, since TypedDict allows that by default). It will also correctly infer that the return value is an int: for DictWithA, get('a') returns int, and for DictWithB, the or will fall back to get('b') which returns int.
Both approaches let you keep your original logic intact while making mypy aware of your domain constraints. The cast method is quicker for small, isolated cases, while the TypedDict union is better for larger codebases where you want to enforce input types more rigorously.
内容的提问来源于stack exchange,提问作者David Michael Gang

