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

Python中‘非未定义’唯一值的类型注解及mypy新惯用法咨询

问题

Python里有个惯用写法:用UNDEFINED = object()作为函数默认参数,用来区分三种场景:

  • 参数未传递(触发do1())
  • 参数传None(触发do2())
  • 参数传有效值(触发do3())

但给这个函数加mypy类型注解时,def do_something(value=int|None|object):完全没用——因为所有对象都是object类型,mypy根本没法区分;而且直接写Literal[UNDEFINED]也不合法。

目前想到的方案是定义私有类_Undefined并实例化UNDEFINED,注解写成def do_something(value=int|None|_Undefined):,但这个方案有两个坑:

  1. 用户可能手动传入新的_Undefined()实例,破坏value is UNDEFINED的判断逻辑
  2. 必须把原来的is判断改成isinstance,违背了原有的惯用写法

想知道现在有没有结合mypy和类型注解处理这种场景的新惯用法?

解决方案

目前社区主流的惯用法是结合私有单例类+Final标记,既满足mypy的类型检查要求,又能保留value is UNDEFINED的安全判断:

1. 带单例保护的私有哨兵类

定义私有类时加入单例逻辑,确保无论怎么调用构造方法,都只会生成一个实例;再用Final标记哨兵实例,防止被重新赋值:

from typing import Final, Union

class _Undefined:
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

UNDEFINED: Final[_Undefined] = _Undefined()

def do_something(value: Union[int, None, _Undefined] = UNDEFINED) -> None:
    if value is UNDEFINED:
        do1()
    elif value is None:
        do2()
    else:
        do3()

这种写法下,用户即使调用_Undefined()也只会拿到和UNDEFINED一样的实例,value is UNDEFINED的判断完全有效,mypy也能正确识别类型。

2. 用类型别名简化注解

如果联合类型写起来太繁琐,可以用TypeAlias给它起个别名,让函数注解更简洁:

from typing import Final, Union, TypeAlias

class _Undefined:
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

UNDEFINED: Final[_Undefined] = _Undefined()
ValueType: TypeAlias = Union[int, None, _Undefined]

def do_something(value: ValueType = UNDEFINED) -> None:
    # 逻辑和之前一致
    ...

3. 枚举类替代方案

另一种更简洁的方式是用enum.Enum创建单例哨兵,枚举天然保证单例,mypy对枚举类型的支持也很完善:

from enum import Enum
from typing import Union, Final

class Sentinel(Enum):
    UNDEFINED = object()

UNDEFINED: Final[Sentinel] = Sentinel.UNDEFINED

def do_something(value: Union[int, None, Sentinel] = UNDEFINED) -> None:
    if value is UNDEFINED:
        do1()
    elif value is None:
        do2()
    else:
        do3()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:34:59