PAYATHON 2026

Python 中如何在不使用 None 的情况下设置占位值(sentinel)?

技术老齐

当 None 也是有效值时,可以用 object() 创建专用的哨兵对象,再用 is 或 is not 判断:

MISSING = object()

value = MISSING

if value is MISSING:
    print("尚未赋值")

哨兵只创建一次,后续初始化和判断都复用这个对象,None 就可以作为正常数据保存。

为什么使用 object()

每次调用 object() 都会创建一个新对象。将其中一个保存为哨兵,就能通过对象身份识别“缺失”或“尚未赋值”的状态。

MISSING = object()

value = None

print(value is MISSING)  # False
print(value is None)     # True

判断哨兵时使用 is,因为要确认变量是否引用了指定对象。== 判断的是相等关系,可能调用对象自定义的 __eq__ 方法。

字符串、0、空列表等在特定场景中也能用作占位值,但可能与有效数据冲突。专门创建的哨兵对象更适合通用场景。

修改输入循环

先创建哨兵,用它初始化变量,再让循环通过身份判断检查变量是否仍是哨兵:

import ast

MISSING = object()
user_input = MISSING

while user_input is MISSING:
    try:
        user_input = ast.literal_eval(
            input("Enter a Python literal: ")
        )
    except (ValueError, SyntaxError):
        print("输入无法解析,请重试。")

输入无效时,赋值不会完成,user_input 仍然引用 MISSING,循环会继续。

输入 None 时,ast.literal_eval() 返回 None,而 None is MISSING 为 False,因此循环正常结束。False、0、空字符串和空容器也都能被接受。

这里捕获了常见的解析错误 ValueError 和 SyntaxError。不要使用裸 except:,否则会连 KeyboardInterrupt 等异常一起捕获,用户按 Ctrl+C 时可能无法正常退出循环。输入流结束引发的 EOFError 也应根据程序需求单独处理。

在数据结构和函数参数中使用

字典的 get() 方法可以接收默认值。把哨兵作为默认值,就能区分“键不存在”和“值为 None”:

MISSING = object()
data = {"name": None}

name = data.get("name", MISSING)
age = data.get("age", MISSING)

print(name is None)     # True:键存在,值为 None
print(age is MISSING)   # True:键不存在

函数参数也可以用哨兵作为默认值,区分“调用者没有传参”和“调用者显式传入 None”:

MISSING = object()

def describe(value=MISSING):
    if value is MISSING:
        return "没有传入参数"
    return f"传入的值是 {value!r}"

print(describe())      # 没有传入参数
print(describe(None))  # 传入的值是 None

树节点中的缺失字段、缓存中尚未计算的结果,也可以用哨兵表示。

使用时需要注意什么

  • 始终复用同一个哨兵。value is object() 每次都会创建新对象,无法判断变量是否引用了先前的哨兵。
  • 不要用 if not value 判断缺失,否则会把 None、False、0 等有效值混在一起。
  • 如果多个模块需要识别同一种缺失状态,应从同一处导入哨兵,避免各自调用 object()。
  • 普通 object() 哨兵适合在内存中通过对象身份判断状态。如果数据需要深拷贝、序列化或跨进程传递,就需要另外设计保留或还原哨兵的方式,不能假定处理后仍是原来的对象。

只为结束输入循环时,可以直接用 break

如果没有其他代码需要读取“尚未赋值”状态,可以在解析成功后直接退出循环:

import ast

while True:
    try:
        user_input = ast.literal_eval(
            input("Enter a Python literal: ")
        )
    except (ValueError, SyntaxError):
        print("输入无法解析,请重试。")
    else:
        break

这种写法不需要哨兵,解析结果为 None 也能正常退出。需要在变量、函数参数或数据结构中长期表示缺失状态时,再使用专用哨兵对象。

备注:内容仅供参考。