Python的in(__contains__)运算符返回一个布尔值,其值既不是True也不是False。

33

果然,数字1并不包含在空元组中。

>>> 1 in ()
False

但是返回的False值并不等于False

>>> 1 in () == False
False

换个角度看,in 操作符返回一个既非 True 也非 Falsebool 类型值:

>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)

然而,如果原始表达式被括号括起来,正常行为将恢复。

>>> (1 in ()) == False
True

或者它的值被存储在一个变量中

>>> value = 1 in ()
>>> value == False
True

这种行为在Python 2和Python 3中都有观察到。

你能解释一下发生了什么吗?

1个回答

45

你遇到了比较运算符链;1 in () == False并不意味着(1 in ()) == False

实际上,比较是链接在一起的,这个表达式的真正含义是:

(1 in ()) and (() == False)

由于(1 in ())已经是false,因此链接表达式的第二个部分将被完全忽略(因为无论something_else的值如何,False and something_else都会返回False)。

请参考比较表达式文档

比较可以任意链接,例如x < y <= z等同于x < y and y <= z,只有y被评估一次(但在两种情况下,当发现x < y为false时,z根本没有被评估)。

顺便说一下,<>==>=<=!=isis notinnot in都是比较运算符(就像已弃用的<>一样)。

一般来说,不要与布尔值进行比较;只需测试表达式本身。如果你必须针对布尔字面值进行测试,至少使用括号和is运算符,TrueFalse都是单例,就像None一样:

>>> (1 in ()) is False
True

当涉及到整数时,情况会变得更加混乱。Python中的bool类型是int的子类1。因此,False == 0为真,True == 1也为真。因此,您可以构建看起来几乎合理的链式操作:

3 > 1 == True

这是正确的,因为3 > 11 == True两个表达式都是真的。但是下面这个表达式:

3 > 2 == True

是错误的,因为2 == True是假的。

1bool 是一个子类 int,出于历史原因;Python 并不总是有 bool 类型,并且像 C 一样重载整数并具有布尔含义。使 bool 成为子类保持了旧代码的兼容性。


谢谢,那很有道理。在现实生活中不会想要与布尔表达式进行比较,但是在试图向某人解释为什么不应该这样做时,我偶然发现了这个好奇心。 - user2949478
2
@user2949478:现在你又有另一个理由了! :-) - Martijn Pieters
这对我来说似乎违反了最少惊讶原则。我可以看到像a is b is None1 < x < 10这样的表达式,但不能混合使用innot inisis not以及(不)等运算符。 - Casey Kuball

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接