如何在不使用None的情况下设置占位符(哨兵值)?

编程语言 2026-07-09

我有一个值需要始终被定义,但它并不总是有意义,我需要一个占位符来表示它还没有有意义的值。通常,我会把 None 作为该占位符值。比如,这段代码本应要求输入一个Python字面量,每次用户输入的不是Python字面量时就再次询问:

import ast

user_input = None
while user_input is None:
    try:
        user_input = ast.literal_eval(input("Enter a Python literal: "))
    except:
        pass

这是一个用例,但我也把 None 用作数据结构、循环等场景中的占位符。

然而,None 有时本身也是一个有意义的值!在上面的例子中,应该允许用户输入 None,但他们不能,因为那样不会终止while循环。在其他情况下,他们可能想把 None 存储在树或类似结构中,因此我就不能使用 None 来表示缺失值。

由于这种模式在跳出循环之外的场景也很有用,我正在寻找一个在 None 不起作用时,针对 None通用替代方案。当 None 不被允许时,我应该使用什么作为占位符值?

解决方案

你可能应该使用一个 哨兵值。在Python 3.14及更早版本中,这通常(尽管并非总是如此)是通过一个裸的 object 与一个同一性检查(is)的组合来实现:

import ast

SENTINEL = object()
user_input = SENTINEL
while user_input is SENTINEL:
    try:
        user_input = ast.literal_eval(input("Enter a Python literal: "))
    except:
        pass

这种模式的好处在于 SENTINEL 完全唯一。没有任何其他用户创建的对象会与之完全相同。因而,与用户提供的输入不会发生冲突;即使另一个 object() 也不会与你的哨兵完全相同。实际上,你可以为不同的用例创建多个独一无二的哨兵:

SENTINEL_1 = object()
SENTINEL_2 = object()
assert SENTINEL_1 is not SENTINEL_2

在与哨兵比较时,请务必始终使用同一性比较(identity),而不是相等比较(equality)(==),否则,某些人可能会在另一个对象上以不良方式重新定义 __eq__

SENTINEL = object()
class AlwaysEqual:
    def __eq__(self, other, /):
        return True
assert SENTINEL is not AlwaysEqual()
assert SENTINEL == AlwaysEqual() # Bad!

在Python 3.15中,使用像这样的 object 将不再是必要的。PEP 661 引入了一个 内置的哨兵类,与旧模式非常相似:

SENTINEL = sentinel("SENTINEL")
assert SENTINEL is not None
SENTINEL_2 = sentinel("SENTINEL_2")
assert SENTINEL is not SENTINEL_2

像一个 object 哨兵一样,新哨兵应始终与 is 进行比较,而不是与 ==

它在类型提示方面也能很好地配合使用(尽管在这种情况下,裸的 None 也可以工作):

SENTINEL = sentinel("SENTINEL")
five_characters: str | SENTINEL = SENTINEL

while five_characters is SENTINEL:
    user_input = input("Enter five characters: ")
    if len(user_input == 5):
        five_characters = user_input

在3.14之前,这种做法将不起作用;通常你需要引入更多开销,比如使用一个类而不是对象作为你的哨兵值。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章