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

如何创建可保留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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:34:51