You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Mypy正确标注可变数据结构?解决类型不兼容报错

问题描述

我编写了如下代码:

def my_foo(x: dict[str, int | float], some_condition: bool) -> None:
    if some_condition:
        x['the_int'] = 1
    else:
        x['the_float'] = 1.0

my_dict = {'some_int': 2}

my_foo(my_dict, True)

Mypy 报错:

error: Argument 1 to "my_foo" has incompatible type "dict[str, int]"; expected "dict[str, int | float]" [arg-type]
note: "Dict" is invariant
note: Consider using "Mapping" instead, which is covariant in the value type

我尝试用MutableMapping标注类型,依然报错:

error: Argument 1 to "my_foo" has incompatible type "dict[str, int]"; expected "MutableMapping[str, int | float]"

请问该如何正确为这类可变数据结构标注类型以通过 Mypy 检查?

解决方案

Mypy 报错的核心原因是字典(dict)和MutableMapping的类型是不变的:dict[str, int]不能被当作dict[str, int | float]使用——因为后者允许添加float类型的值,而前者的所有值都被约束为int,函数my_foo在某些分支下会向字典中写入float,这会破坏原字典的类型一致性。

根据你的需求,有几种合规的处理方式:

1. 明确标注原字典的类型为兼容类型

如果my_dict后续确实可能包含int或float类型的值,直接将其类型标注为dict[str, int | float],从源头解决类型不匹配:

my_dict: dict[str, int | float] = {'some_int': 2}
my_foo(my_dict, True)

2. 传入字典的副本而非原字典

如果不想改变my_dict的类型约束(即它始终只包含int值),可以传入原字典的副本,这样函数修改的是副本,不会影响原字典的类型:

my_foo(my_dict.copy(), True)

3. 使用泛型约束函数参数(更灵活的场景)

如果函数需要处理更广泛的可变映射类型,同时保证类型安全,可以用TypeVar定义泛型,约束值类型为int | float的超类型:

from typing import MutableMapping, TypeVar

T = TypeVar('T', int, float)

def my_foo(x: MutableMapping[str, T], some_condition: bool) -> None:
    if some_condition:
        x['the_int'] = 1  # 1 属于 int,兼容 T 的两种可能
    else:
        x['the_float'] = 1.0  # 1.0 属于 float,兼容 T 的两种可能

my_dict = {'some_int': 2}
my_foo(my_dict, True)

这种方式下,Mypy 会推断T为int,函数写入的1符合int类型,不会触发报错;如果传入的字典是dict[str, float],T会被推断为float,写入的1.0也符合类型约束。

4. 重构函数为无副作用的纯函数(推荐)

如果允许修改函数逻辑,最好避免直接修改传入的可变对象,而是返回一个新的字典,这样完全规避可变性带来的类型问题:

def my_foo(x: dict[str, int], some_condition: bool) -> dict[str, int | float]:
    new_dict = x.copy()
    if some_condition:
        new_dict['the_int'] = 1
    else:
        new_dict['the_float'] = 1.0
    return new_dict

my_dict = {'some_int': 2}
updated_dict = my_foo(my_dict, True)

这种方式下,原字典的类型不受影响,新字典的类型明确标注为包含int或float,完全符合 Mypy 的类型检查规则。

内容的提问来源于stack exchange,提问作者user2857891

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:57:06