Python 中 if not value 与 is None 的区别
副标题: if not line 判断的是“这个值是否为假值”,if line is None
判断的是“这个值是否就是 None”。两者可能在某些输入上得到相同结果,但表达的业务语义并不相同。
适读人群: 正在学习 Python 条件判断,或需要处理文本、接口参数和可选字段的开发者
阅读时间: 6 分钟
从文档解析中的空行过滤说起
逐行解析文档时,经常会看到下面的代码:
for raw_line in lines:
line = raw_line.strip()
if not line:
continue
# 继续处理非空内容
这里为什么使用:
if not line:
而不是:
if line is None:
关键不在于哪种写法更简短,而在于程序此时要识别的是空字符串,不是
缺失值 None。
先跟踪 line 是怎么产生的
这一段代码的执行顺序是:
raw_line = " "
line = raw_line.strip()
str.strip() 会删除字符串首尾的空白字符,并返回一个字符串:
assert " 投标人资格要求 ".strip() == "投标人资格要求"
assert " ".strip() == ""
assert "".strip() == ""
只包含空格的行经过 strip() 后会变成空字符串 "",不会变成 None。
因此后面的条件实际需要回答:
line是不是一个空字符串?
空字符串在 Python 的条件判断中属于假值,所以:
assert not ""
if not line 条件成立,continue 跳过当前循环,空行便不会进入后续解析逻辑。
if not line 判断的是真值
Python 会在 if、while 和布尔运算中对对象进行真值测试。常见的假值包括:
assert not None
assert not False
assert not 0
assert not 0.0
assert not ""
assert not []
assert not {}
assert not set()
所以:
if not value:
表达的是:
如果
value是任意一种假值,就执行这个分支。
它不是专门判断 None,也不是专门判断空字符串。
在文档解析示例里,类型标注 lines: list[str] 和前面的 strip() 已经把
line 的类型限定为 str。对字符串而言,假值只有 "",因此
if not line 在这里可以准确表达“跳过空行”。
if line is None 判断的是对象身份
None 是 Python 用来表示“没有值”或“值尚未提供”的单例对象。推荐使用
is None 判断一个变量是否正是这个对象:
value = None
assert value is None
空字符串不是 None:
line = ""
assert line is not None
assert not line
因此,把原代码改成下面这样会漏掉空行:
line = " ".strip()
if line is None:
print("跳过")
# line 是 "",条件不成立,程序不会跳过
is None 回答的是“值是否缺失”,而不是“字符串是否为空”。
空值与缺失值不是一回事
这两个判断的选择,本质上取决于业务上是否需要区分不同状态。
| 程序意图 | 推荐写法 |
|---|---|
| 跳过空字符串 | if not line: |
| 只接受严格的空字符串,且变量可能是其他类型 | if line == "": |
| 判断调用方是否没有提供值 | if value is None: |
区分 None、""、0 和 False | 分别显式判断 |
| 把所有假值统一视为“无内容” | if not value: |
例如,一个可选备注字段可能需要区分三种状态:
note = None # 没有提供备注
note = "" # 明确提供了空备注
note = "待复核" # 提供了实际内容
如果业务需要保留这种区别,就不能用一个 if not note 把前两种状态合并。
当输入既可能是 None,也可能是字符串
假设外部数据允许传入 None,类型应明确写成 str | None。此时必须先处理
None,再调用字符串方法:
def normalize_line(raw_line: str | None) -> str | None:
if raw_line is None:
return None
line = raw_line.strip()
if not line:
return None
return line
顺序不能反过来:
raw_line = None
line = raw_line.strip() # AttributeError
这里的两个条件承担不同职责:
raw_line is None:输入值不存在
not line:清理后的字符串为空
如果调用方需要区分“没有输入”和“输入了一行空白”,就不应该都返回 None,
而应为两种状态设计不同的返回结果。
最容易出现的问题:变量不只有字符串
if not value 很方便,但当变量可能包含多种类型时,它可能把有意义的数据一起过滤掉。
数字 0 可能是有效值
quantity = 0
if not quantity:
print("没有数量")
这段代码把合法的数量 0 当成了缺失值。如果 None 才代表“未填写”,应该写:
if quantity is None:
print("没有填写数量")
False 可能是明确选择
mandatory = False
if not mandatory:
print("没有填写是否硬性要求")
这里无法区分“明确选择否”和“没有填写”。若字段类型是 bool | None,应显式判断:
if mandatory is None:
print("没有填写")
elif mandatory:
print("硬性要求")
else:
print("非硬性要求")
所以,使用 if not value 前应先问:
这个变量的所有假值,是否都可以被当成同一种状态?
只有答案为“是”时,合并判断才是安全的。
一个可运行的完整示例
下面的函数接收字符串列表,去掉首尾空白并过滤空行:
def collect_non_empty_lines(lines: list[str]) -> list[str]:
result: list[str] = []
for raw_line in lines:
line = raw_line.strip()
if not line:
continue
result.append(line)
return result
sample_lines = [
"第一标段\n",
" ",
" 投标人须具备安全生产许可证 ",
"",
]
parsed_lines = collect_non_empty_lines(sample_lines)
assert parsed_lines == [
"第一标段",
"投标人须具备安全生产许可证",
]
print(parsed_lines)
运行后输出:
['第一标段', '投标人须具备安全生产许可证']
在这个函数中,if not line 是合适的,因为函数契约已经确定输入是
list[str],而且业务目标就是过滤清理后的空字符串。
选择时记住两句话
if not value:所有假值都进入分支。
if value is None:只有缺失值 None 进入分支。
不要根据代码长短选择,而要根据数据类型和业务语义选择。
对于本文开头的文档解析代码,推导过程很明确:
raw_line 是字符串
-> strip() 返回字符串
-> 空白行变成 ""
-> 当前目标是过滤空字符串
-> 使用 if not line
如果以后允许输入 None,就应修改类型契约,并在调用 strip() 之前显式使用
is None 处理它,而不是简单替换原有条件。
参考资料
可以尝试把完整示例中的输入类型改成 list[str | None],先观察直接调用
strip() 时的错误,再为 None 和空字符串分别补上符合业务含义的处理分支。