为什么Python中没有显式的空值检查(例如`is Empty`)
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 个回答
我同意,有时候用 if foo:
这段代码并不能很清楚地告诉读代码的人,我其实是在检查它是否为空。在这种情况下,我会用 if len(foo):
,这样就很明确了。
我完全同意Alex的看法,认为 is Empty
这种写法不符合Python的风格。
在 if foo:
和 if not foo:
中的多态性并不是“隐式与显式”的违反:它是明确地把判断真假的任务交给了被检查的对象。也就是说,判断的意义(以及最佳的检查方式)显然要依赖于对象的类型,因此风格指南要求这种委托——如果应用层的代码自以为是地认为自己比对象更了解情况,那简直是愚蠢至极。
此外,X is Whatever
总是意味着 X 是完全相同的对象,和 Whatever 一模一样。为 Empty
或其他特定的 Whatever
值特意开个例外,那简直是荒谬——想象一下,没有比这更不符合 Python 风格的做法了。而“完全相同的对象”显然是传递的——所以你永远不能有不同的空列表、空集合、空字典……恭喜你,你刚刚设计了一个完全不可用且毫无用处的语言,所有空容器都疯狂地“合并”成一个空容器对象(想象一下,当有人试图修改一个空容器时会发生什么……?!)。
为什么没有 is Empty
这个东西,其实一旦你明白 is
操作符的作用,就会发现这个问题很简单。
根据 Python 手册 的解释:
is
和is not
这两个操作符是用来检查对象是否相同的:x is y
只有在x
和y
是同一个对象时才为真。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
操作符是用来检查对象是否相同的,这就意味着 a
和 b
必须和 Empty
是同一个对象。这就意味着 a
和 b
也必须是相同的,但它们实际上并不是:
>>> 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__()
方法,返回一个布尔值。