Python技术迷

为什么不建议你用去 “! = null” 做判空?

x != null 这个判空写法,在 Python 里不只是别扭,很多时候还是错的。

刚接触 Python 的人,尤其是从 Java、C# 这类语言切过来的,特别容易手一抖写出这种代码:

if x != null: process(x)

这段代码先别谈好不好,先说结果:直接报错。

NameError: name 'null' is not defined

Python 里根本没有 null,对应的是 None。这事看着像语法问题,其实后面还有一层更容易踩坑的:Python 判空这件事,很多人不是写错了一个词,而是整个判断思路都带着别的语言的惯性。 参考风格见:

先把最容易写错的地方摆出来

很多人会这么改:

if x != None: process(x)

这次倒是能跑了,但我平时不太建议这么写。

不是说它一定错,而是它在 Python 里不够地道,而且碰到一些“像空又不完全是空”的值时,代码很容易越写越歪。

举个很常见的清洗字段的脚本。

我以前处理导出数据时,经常会写这种逻辑:

row = { "user_id": 1001, "email": "", "age": 0, "remark": None }

有的人为了过滤掉“没值”的字段,会这样写:

for k, v in row.items(): if v != None: print(k, v)

输出是:

user_id 1001 email age 0

这就有意思了。

如果你的目标只是过滤掉 None,这段没问题。 但如果你的目标其实是“过滤空值”,那空字符串 ""、数字 0、空列表 [] 都还在。

很多线上脚本最后写成一坨 != None、!= ''、!= [],其实不是 Python 难用,是一开始判空就没想明白:你到底是在判“是不是 None”,还是在判“有没有内容”。

这两个不是一回事。

Python 里别拿 null,要拿 None

先把基础结论说掉。

Python 只有 None,没有 null:

x = Noneif x is None: print("空值")

这里我一般直接用 is None,而不是 == None,更不会写 != null。

原因不复杂,None 在 Python 里是个单例对象,判它最稳的方式就是身份判断,不是值比较。

x = Noneprint(x == None)   # True print(x is None)   # True

这俩表面看起来一样,但真到自定义对象、重载比较运算的时候,差别就出来了。

为什么我不建议写 != None

因为 != 走的是值比较,背后会调对象自己的比较逻辑。这个地方有坑,而且坑不算小。

看个很短的例子:

class DirtyValue: def eq(self, other): return Truex = DirtyValue()print(x == None)   # True print(x is None)   # False

这代码在真实业务里不一定长这样,但意思已经够明显了: == None 和 != None 依赖对象怎么实现比较;is None 只看它到底是不是那个 None 对象。

你清洗接口返回值、处理 ORM 对象、接第三方库结果时,这种差异是可能真撞上的。

所以我自己写代码,一般会这么分:

如果我要判断“这个值是不是 None”,我写:

if x is not None: process(x)

如果我要判断“这个值是不是空的、假的、没内容”,我写:

if x: process(x)

这两句长得像,语义完全不一样。

is not None 和 if x: 不是一个意思

这个地方特别容易混。

比如接口字段清洗,经常会遇到这几种值:

values = [None, "", 0, False, [], {}, "ok", 12]

你跑一下:

for v in values: print(v, v is not None, bool(v))

大概就是这个效果:

None   False False ""     True  False 0      True  False False  True  False []     True  False {}     True  False "ok"   True  True 12     True  True

差别就很明显了。

1)你要的是“字段存在,但允许 0”

比如订单金额、库存、重试次数这种字段,0 是有效值,不是空值。

那你不能写:

if retry_count: save(retry_count)

因为 0 会被当成 False 干掉。

这时候应该写:

if retry_count is not None: save(retry_count) 2)你要的是“字符串非空”

那 None 和 "" 都不想要。

这时候写:

if name: save(name)

就比 name != None 靠谱得多。 因为你本来就不只是想排除 None,你是想排除“没内容”。

我见过最常见的一种脏写法

很多脚本到后面会长成这样:

if val != None and val != '' and val != [] and val != {}: handle(val)

这类代码我一般看见就想改。

不是它不能跑,是它越补越没头。

今天加个 '',明天补个 [],后天再补个 'null'、'None'、'N/A'。最后你会发现,自己不是在判空,是在跟脏数据互殴。

这种时候,最好先把规则掰直:

只是判断是不是 None if val is not None: handle(val) 判断是不是“有内容的字符串” if isinstance(val, str) and val.strip(): handle(val) 判断容器里是不是有元素 if val: handle(val) 字段清洗时统一归一

这个我平时更常用一点,先收口,再用。

def normalize(v): if v is None: return None if isinstance(v, str): v = v.strip() return v or None return vcleaned = {k: normalize(v) for k, v in payload.items()}

后面再判断就简单很多:

if cleaned["nickname"] is not None: ... 再说回 null 这件事,问题不只是写错单词

很多人是从 JSON 里把习惯带进来的。

JSON 长这样:

{ "name": null }

然后脑子里就默认代码里也该写 null。

但 JSON 是 JSON,Python 是 Python。 JSON 反序列化到 Python 之后,那个 null 会变成 None:

import jsondata = json.loads('{"name": null}') print(data)               # {'name': None} print(data["name"] is None)  # True

所以你在处理接口返回、消息队列消息、配置文件的时候,别拿 JSON 的字面量习惯直接往 Python 代码里套。

这个坑不大,但特别烦,尤其是在临时脚本、日志分析脚本里,复制一段示例数据过来,人顺手就写成了 null。

实战里怎么写,我一般这么分 场景一:接口字段,只想排除 None timeout = config.get("timeout")

if timeout is not None: client.timeout = timeout

这里不能写 if timeout:。 因为 timeout = 0 可能是明确配置。

场景二:清洗用户输入,空串也不要 nickname = form.get("nickname")

if nickname and nickname.strip(): user.nickname = nickname.strip()

这里用 is not None 不够,因为 " " 这种值你大概率也不想要。

场景三:批量导入,只保留真正有值的字段 def has_value(v): if v is None: 

return False if isinstance(v, str) and not v.strip(): return False return Truerow = { "name": "东哥", "mobile": "", "age": 0, "remark": None }clean_row = {k: v for k, v in row.items() if has_value(v)} print(clean_row)

输出:

{'name': '东哥', 'age': 0}

这种写法就比到处散着 != None 顺手得多。

x != null 在 Python 里,第一层就是错,null 根本不存在。 真改成 x != None 之后,也不算好写法,因为它把“判 None”和“判空”这两件事混在一起了,而且还依赖对象的比较行为。

这类判断我平时基本只留两种:

if x is not None:

和

if x:

前者判断“是不是 None”,后者判断“有没有内容”。

别混着用。这个地方一混,后面处理 0、空串、空列表、False 的时候,代码就开始拧巴了。 Python 判空本来不复杂,复杂的是拿别的语言习惯硬往里塞。