You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Mypy报赋值类型不兼容存疑:str为Union成员为何报错?

为什么Mypy会报Union类型赋值错误?

这是个很常见的静态类型检查困惑,我来帮你拆解清楚问题所在:

核心原因:Mypy无法静态推断get('timeout')的具体返回类型

你的get函数标注的返回类型是Union[str, Dict[str, str]]——这告诉Mypy:不管你传什么参数,这个函数的返回值都可能是字符串或者字典的其中一种。哪怕你传的是单个字符串'timeout',Mypy也没办法从当前的类型标注里知道这次调用一定会返回str,它只能看到返回类型是二选一的联合类型,所以直接赋值给str类型的变量timeout就会触发类型不兼容的错误。

你的代码里还有两个关键问题需要修正

1. get_single函数的参数类型标注错误

get_single的定义:

def get_single(field: List[str], config_data) -> str:
    result = config_data.get(field)
    # ...

这里的field参数标注成了List[str],但实际上你调用它的时候传的是单个字符串(当fieldsstr时),而且config_data.get()接收的键也应该是str类型,这个错误的标注不仅会导致潜在的运行时问题,还会让get函数的类型逻辑更混乱。

2. get函数的类型标注没有利用重载来区分输入输出

你需要给get函数添加类型重载,让Mypy知道:当输入是str时返回str,当输入是List[str]时返回Dict[str, str],而不是用一个统一的Union类型。

修复后的代码示例

from typing import List, Dict, Union, overload

def get_multiple(fields: List[str], config_data) -> Dict[str, str]:
    config_results = {k: v for k, v in config_data.items() if k in fields}
    log_missing_fields(fields, config_results)
    return config_results

# 修正get_single的参数类型
def get_single(field: str, config_data) -> str:
    result = config_data.get(field)
    if result is None:
        log.warning('The following field is missing: %s', field)
    return result

# 添加重载标注,告诉Mypy不同输入对应不同输出
@overload
def get(fields: str) -> str: ...

@overload
def get(fields: List[str]) -> Dict[str, str]: ...

def get(fields: Union[str, List[str]]) -> Union[str, Dict[str, str]]:
    log.debug('Retrieving values %s from config', str(fields))
    config_data = read_config()
    get_data = get_multiple if isinstance(fields, list) else get_single
    return get_data(fields, config_data)

def get_ts_url() -> str:
    # 现在Mypy能正确推断timeout是str类型,不会报错了
    timeout = get('timeout')
    log.info('Trying to connect the servers.')
    with db_session() as db_handler:
        url = scan_availability(db_handler, int(timeout))
        if url:
            return url
    log.critical("Could not find available servers.")
    raise ConnectionError("Could not find available servers.")

额外说明:如果暂时不想用重载,还有两种临时解决办法

  • 类型忽略跳过检查:
    timeout = get('timeout')  # type: ignore
    
  • 类型断言明确告诉Mypy返回值类型:
    timeout = get('timeout')
    assert isinstance(timeout, str)
    

但这两种方法都不如重载优雅,因为重载能让类型系统自动完成推断,不需要手动干预。

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

火山引擎 最新活动