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 也能正常退出。需要在变量、函数参数或数据结构中长期表示缺失状态时,再使用专用哨兵对象。
备注:内容仅供参考。
