您被智能类型推理咬了一口.为了清楚起见,我将摆脱对FastAPI的依赖,并在一个更简单的 case 中重现:
foo = {"foo": ["bar"]}
foo["bar"] = "baz"
mypy
(或pylance
)错误我附近没有Pylance,所以让我们坚持mypy
-问题是一样的,措辞可能会有所不同.
E:赋值中的类型不兼容(表达式的类型为"str",目标的类型为"list[str]")[赋值]
现在应该清楚了:foo
在第一次赋值时被推断为dict[str, list[str]]
.我们可以加reveal_type
来确认,这是the playground.现在,类型判断器理所当然地抱怨道:您正试图将值类型为list[str]
的dict中的值设置为str
.馊主意.
那么,现在怎么办?您需要告诉TypeChecker,您不希望推理如此精确.您的选项很少(以下任一选项都可以):
from typing import Any, TypedDict, NotRequired
class Possible(TypedDict):
foo: list[str]
bar: NotRequired[str]
foo: dict[str, Any] = {"foo": ["bar"]}
foo: dict[str, object] = {"foo": ["bar"]}
foo: dict[str, list[str] | str] = {"foo": ["bar"]}
foo: Possible = {"foo": ["bar"]}
有什么区别?
object
是一种"就是某样东西"的类型--一种你几乎一无所知的类型.您不能将对象相加或相乘,也不能将它们传递给需要某些特定类型的函数,但可以打印它.我认为这个解决方案最适合您的用例,因为之后您不需要对判据做任何操作.
Possible
TypedDict
是这里的亚军:如果这个字典定义了一些重要的 struct ,你在以后的处理中经常使用这些 struct ,你最好清楚地说出每个键的意思.
Any
意味着"离我远点,我知道我在做什么".这可能是一个很好的解决方案,如果你稍后使用这个字典-object
将需要大量的类型缩小才能使用,而Any
就可以了.
- UNION解决方案是最差的,除非您的键是真正动态的(没有语义意义).每当您try 将值用作两种联合类型中的任何一种时,类型判断器都会向您喊话-它总是可以是另一种类型.(这不是一个通用的建议:对于键不表示JS对象之类的 struct 的情况,
dict[str, int | str]
可能是一个很好的类型)
因此,在您的特定情况下
@app.get("/items/")
async def read_items(q: str | None = None):
results: dict[str, object] = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q}) # warning here
return results
应该会通过.
既然你和其他回答者被过载误导了,下面是MutableMapping.update
(dict
个子类MutableMapping
在存根中)在typeshed中的样子:
class MutableMapping(Mapping[_KT, _VT]):
... # More methods
@overload
def update(self, __m: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ...
@overload
def update(self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
@overload
def update(self, **kwargs: _VT) -> None: ...
根据皮兰斯的说法,我不知道为什么"超载2是最接近的匹配",但你当然应该击中第一个.