如何用常量定义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
相关产品推荐
相关产品推荐

