如何创建可保留Literal类型的Python泛型函数?
解决Python泛型函数保留Literal类型并移除None的问题
一、解决Literal被推断为父类型的问题
你遇到的Literal["a", "b"]被推断为str的核心原因是:未给TypeVar添加约束,类型检查器默认会将字面量类型拓宽到其基础父类型。只需给TypeVar指定bound约束,即可保留原Literal类型:
from typing import TypeVar, Literal # 绑定TypeVar为str的子类型,包含Literal类型 TLiteralValue = TypeVar("TLiteralValue", bound=str) def return_same_type(input: TLiteralValue) -> TLiteralValue: return input before: Literal["a", "b"] = "a" after = return_same_type(before) # 现在类型推断为Literal["a", "b"]
如果使用pyright作为类型检查器,默认会更严格地保留Literal类型,无需额外配置;若用mypy,建议启用strict=True配置,进一步强化类型推断精度。
二、实现“移除None并保留原类型”的泛型函数
针对输入为T | None、返回为T的需求,分Python版本给出实现方案:
1. Python 3.9 版本
Python 3.9标准库无TypeGuard,需借助typing_extensions库实现类型守卫,或用cast做类型断言:
方式一:使用TypeGuard(推荐)
from typing import TypeVar, Union, Literal from typing_extensions import TypeGuard T = TypeVar("T") # 类型守卫:判断输入非None时,将类型收窄为T def is_not_none(input: Union[T, None]) -> TypeGuard[T]: return input is not None # 使用示例 before: Literal["a", "b"] | None = "a" if is_not_none(before): after = before # 此时after类型为Literal["a", "b"]
方式二:使用cast(直接返回非None值)
from typing import TypeVar, Union, Literal, cast T = TypeVar("T") def remove_none(input: Union[T, None]) -> T: assert input is not None, "输入不能为None" return cast(T, input) # 使用示例 before: Literal["a", "b"] | None = "a" after = remove_none(before) # 类型推断为Literal["a", "b"]
2. Python 3.10+ 版本
Python 3.10将TypeGuard纳入标准库,同时支持|运算符替代Union,代码更简洁:
from typing import TypeVar, Literal, TypeGuard T = TypeVar("T") def is_not_none(input: T | None) -> TypeGuard[T]: return input is not None # 使用示例 before: Literal["a", "b"] | None = "a" if is_not_none(before): after = before # 类型为Literal["a", "b"]
3. Python 3.11+ 版本
Python 3.11新增TypeIs(原typing_extensions.TypeIs),可更精确地定义类型关系,适合复杂场景:
from typing import TypeVar, Literal, TypeIs T = TypeVar("T") def remove_none(input: T | None) -> TypeIs[T]: assert input is not None return input
三、第三方库辅助方案
若使用pydantic等数据验证库,可借助TypeAdapter实现类型转换与验证,同时保留精确类型提示:
from pydantic import TypeAdapter from typing import Literal adapter = TypeAdapter(Literal["a", "b"]) before: Literal["a", "b"] | None = "a" after = adapter.validate_python(before) # 类型为Literal["a", "b"]
内容的提问来源于stack exchange,提问作者yaexiste2
相关产品推荐
相关产品推荐

