为什么Python中没有显式的空值检查(例如`is Empty`)

23 投票
5 回答
2942 浏览
提问于 2025-04-16 02:49

Python的哲学中提到“明确比隐含更好”。但是,检查一个东西是否为空的“pythonic”方式是用隐含的布尔值:

if not some_sequence:
    some_sequence.fill_sequence()

如果some_sequence是一个空的序列,这个表达式会返回真,但如果它是None或者0时也会返回真。

我们来对比一下一个理论上明确的空检查:

if some_sequence is Empty:
    some_sequence.fill_sequence()

如果变量名选择得不太好,使用隐含布尔值来检查是否为空就会更加混乱:

if saved:
    mess_up()

再对比一下:

if saved is not Empty:
    mess_up()

另外可以参考:“Python: 检查一个列表是否为空的最佳方法是什么?”。我觉得很讽刺的是,点赞最多的回答却说隐含的方式是pythonic。

那么,是否有更深层的原因解释为什么没有像is Empty这样的明确空检查呢?

5 个回答

6

我同意,有时候用 if foo: 这段代码并不能很清楚地告诉读代码的人,我其实是在检查它是否为空。在这种情况下,我会用 if len(foo):,这样就很明确了。

我完全同意Alex的看法,认为 is Empty 这种写法不符合Python的风格。

21

if foo:if not foo: 中的多态性并不是“隐式与显式”的违反:它是明确地把判断真假的任务交给了被检查的对象。也就是说,判断的意义(以及最佳的检查方式)显然要依赖于对象的类型,因此风格指南要求这种委托——如果应用层的代码自以为是地认为自己比对象更了解情况,那简直是愚蠢至极。

此外,X is Whatever 总是意味着 X 是完全相同的对象,和 Whatever 一模一样。为 Empty 或其他特定的 Whatever 值特意开个例外,那简直是荒谬——想象一下,没有比这更不符合 Python 风格的做法了。而“完全相同的对象”显然是传递的——所以你永远不能有不同的空列表、空集合、空字典……恭喜你,你刚刚设计了一个完全不可用且毫无用处的语言,所有空容器都疯狂地“合并”成一个空容器对象(想象一下,当有人试图修改一个空容器时会发生什么……?!)。

15

为什么没有 is Empty 这个东西,其实一旦你明白 is 操作符的作用,就会发现这个问题很简单。

根据 Python 手册 的解释:

isis not 这两个操作符是用来检查对象是否相同的:x is y 只有在 xy 是同一个对象时才为真。x is not y 则是相反的意思。

这意味着 some_sequence is Empty 是在检查 some_sequence 是否和 Empty 是同一个对象。你提到的那种方式是行不通的。

看看下面这个例子:

>>> a = []
>>> b = {}

现在假设 Python 中有这个 is Empty 的写法:

>>> a is Empty
True
>>> b is Empty
True

但是因为 is 操作符是用来检查对象是否相同的,这就意味着 ab 必须和 Empty 是同一个对象。这就意味着 ab 也必须是相同的,但它们实际上并不是:

>>> a is b
False

所以,回答你的问题“为什么 Python 中没有 is Empty?”的原因是:因为 is 是用来检查对象身份的。

如果想要有 is Empty 这个写法,你要么得修改 is 操作符的意思,要么就得创建一个神奇的 Empty 对象,这个对象能检测空的集合,并且和它们是同一个对象。

与其问为什么没有 is Empty,不如问为什么没有内置函数 isempty(),这个函数调用一个特殊的方法 __isempty__()

所以,我们不是用隐式的布尔值来判断是否为空:

if saved:
  mess_up()

而是用显式的空检查:

if not isempty(saved):
  mess_up()

在这里,saved 的类实现了一个 __isempty__() 方法,里面有合理的逻辑。

我觉得这样比用隐式的布尔值来检查是否为空要好得多。

当然,你可以很容易地定义自己的 isempty() 函数:

def isempty(collection):
  try:
    return collection.__isempty__()
  except AttributeError:
    # fall back to implicit booleaness but check for common pitfalls
    if collection is None:
      raise TypeError('None cannot be empty')
    if collection is False:
      raise TypeError('False cannot be empty')
    if collection == 0:
      raise TypeError('0 cannot be empty')
    return bool(collection)

然后为你的所有集合类定义一个 __isempty__() 方法,返回一个布尔值。

撰写回答