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

如何用常量定义Literal类型并解决mypy类型区分问题?

问题与解决方案

一、用常量变量定义Literal类型的无重复方案

想要用字符串常量直接作为Literal的参数(比如Literal[FOO, BAR]),但mypy会报错;如果把常量定义成Literal["foo"],运行时变量又会变成Literal对象而非普通字符串,怎么避免重复定义?

可以用LiteralString结合Final解决,Python 3.10+原生支持,低版本可通过typing_extensions导入:

from dataclasses import dataclass
from typing import Final, Literal, LiteralString

# Final标记常量不可修改,LiteralString告诉mypy这是字面量字符串
FOO: Final[LiteralString] = "foo"
BAR: Final[LiteralString] = "bar"

@dataclass
class Event():
    # 直接用常量作为Literal参数,mypy会识别为等价于Literal["foo", "bar"]
    name: Literal[FOO, BAR]

# 运行时变量是普通字符串,不影响业务逻辑
event = Event(name=FOO)
print(event.name)  # 输出 "foo"

这种方式既不用重复写字符串字面量,变量本身保持正常字符串属性,mypy也能正确识别类型约束。

二、Enum实现后的类型窄化问题解决

用Enum定义事件类型后,mypy无法通过event.name == FooBar.FOO的判断自动窄化event.value的类型,导致两个分支都报错。可以用两种方法修复:

方法1:自定义类型守卫函数

编写类型守卫函数,明确告诉mypy如何判断事件的具体类型:

from dataclasses import dataclass
from enum import Enum
from typing import Literal, TypeGuard

class FooBar(Enum):
    FOO = "foo"
    BAR = "bar"

@dataclass
class StringEvent:
    name: Literal[FooBar.FOO]
    value: str

@dataclass
class NumberEvent:
    name: Literal[FooBar.BAR]
    value: int

def is_string_event(event: StringEvent | NumberEvent) -> TypeGuard[StringEvent]:
    return event.name == FooBar.FOO

def is_number_event(event: StringEvent | NumberEvent) -> TypeGuard[NumberEvent]:
    return event.name == FooBar.BAR

def handle_event(event: StringEvent | NumberEvent):
    if is_string_event(event):
        event.value.upper()  # mypy不报错,已识别为str类型
    if is_number_event(event):
        event.value.upper()  # mypy报错,int没有upper方法

方法2:使用match语句(Python 3.10+)

利用Python 3.10+支持的结构化模式匹配,mypy能自动识别类型并完成窄化:

from dataclasses import dataclass
from enum import Enum
from typing import Literal

class FooBar(Enum):
    FOO = "foo"
    BAR = "bar"

@dataclass
class StringEvent:
    name: Literal[FooBar.FOO]
    value: str

@dataclass
class NumberEvent:
    name: Literal[FooBar.BAR]
    value: int

def handle_event(event: StringEvent | NumberEvent):
    match event:
        case StringEvent(name=FooBar.FOO, value=val):
            val.upper()  # mypy不报错
        case NumberEvent(name=FooBar.BAR, value=val):
            val.upper()  # mypy报错:int没有upper方法

内容的提问来源于stack exchange,提问作者Quint van Dijk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 01:04:54