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

如何创建可同时作为类型与值使用的单例对象(类似None)

实现可用于类型提示的自定义NotSet值

我希望定义一个自定义的NotSet值,能像None一样直接用于类型提示,示例代码期望如下:

NotSet = ?

class Client:
    def partial_update(
            self,
            obj_id: int,
            obj_field: int | None | NotSet = NotSet,
            ...
    ):
        # 不更新未显式指定的字段
        if obj_field is NotSet:
           # 不更新obj_field
        else: 
           # 更新obj_field
        ...

不够理想的解决方案

1. 使用None

  • 问题:None本身已用于业务逻辑(字段支持可空),无法区分“未指定字段”和“指定字段为null”两种场景。

2. 使用内置ellipsis

from types import EllipsisType

def partial_update(
   obj_field: int | None | EllipsisType = ...,
):
   pass
  • 缺点:
    • 语义不明确,...通常用于省略参数或表示任意类型,无法直观体现“未设置”的含义;
    • 无法直接写int | None | ...作为类型提示,必须引用EllipsisType。

3. 使用自定义单例类

class NotSetType:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

NotSet = NotSetType()

def partial_update(
   obj_field: int | None | NotSetType = NotSet,
):
   pass
  • 缺点:这是最接近需求的方案,但类型提示必须写NotSetType,无法直接使用NotSet,不符合“像None一样”的预期。

改进后的最优方案

我们可以通过类型别名结合单例模式,让NotSet同时作为单例实例和类型提示标识:

from typing import TypeAlias

class NotSetType:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

    def __repr__(self) -> str:
        return "NotSet"

# 让NotSet既是单例实例,又是指向NotSetType的类型别名
NotSet: TypeAlias = NotSetType()

# 验证类型提示和运行逻辑
class Client:
    def partial_update(
            self,
            obj_id: int,
            obj_field: int | None | NotSet = NotSet,
    ):
        if obj_field is NotSet:
            print("不更新obj_field")
        else:
            print(f"更新obj_field为{obj_field}")

# 测试调用
client = Client()
client.partial_update(1)                  # 输出:不更新obj_field
client.partial_update(1, obj_field=None)  # 输出:更新obj_field为None
client.partial_update(1, obj_field=5)     # 输出:更新obj_field为5

在Python 3.12+中,还可以用更简洁的type语句定义类型别名:

class NotSetType:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

NotSet = NotSetType()
type NotSet = NotSetType

这样做的好处:

  • 类型提示中直接写NotSet即可,和None的使用方式完全一致;
  • 运行时通过is判断obj_field is NotSet能准确识别未设置的字段;
  • 语义清晰,NotSet明确表示“字段未被显式指定”,和业务逻辑中的None(字段设为null)完全区分。

内容的提问来源于stack exchange,提问作者Mikhail M.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 04:48:26