您混合了两个概念:相等测试和真值测试。它们在 Python 中是不一样的。
我认为引发这个问题的原因是 Python在你这样做时会进行隐式强制转换if something
(它将某物强制转换为bool
),但在你这样做时它不会进行隐式强制转换something1 == something2
。
Python 的数据模型实际上解释了这些操作是如何完成的:
- 它首先检查对象是否实现了该
__bool__
方法,如果实现了,则使用返回的布尔值。
- 如果它没有定义
__bool__
方法,它会查看该__len__
方法。如果它被实现,它将使用len(obj) != 0
.
- 如果两者都没有,则考虑该对象
True
。
对于整数,该__bool__
方法返回True
,除非整数值为0
(然后它是False
)。
另一方面, Ellipsis 对象(...
是 Ellipsis 对象)没有实现__bool__
,__len__
所以它总是True
。
相等性测试依赖于两个参数的__eq__
方法。它更像是一个操作链:
__eq__
当第二个操作数作为参数传递时,它检查第一个操作数是否实现。
- 如果没有,则检查第二个操作数是否
__eq__
在第一个操作数作为参数传递时实现。
- 如果不是,则 Python 检查对象身份(如果它们是同一个对象 - 类似于 C 语言中的指针比较)
这些操作的顺序可能会有所不同。1
对于内置的 Python 类型,这些操作是显式实现的。例如int
egers 实现__eq__
但CHECK_BINOP
确保NotImplemented
如果另一个不是int
eger 则返回。
该Ellipsis
对象根本没有实现__eq__
。
因此,当您比较整数和省略号时,Python 将始终回退到对象标识,因此它将始终返回False
.
另一方面,bool
eans 是 egers 的子类,int
因此它们实际上是与之比较的(毕竟int
它们是另一个)。int
布尔值实现为1
( True
) 和0
( False
)。所以他们比较相等:
>>> 1 == True
True
>>> 0 == False
True
>>> 1 == False
False
>>> 0 == True
False
尽管源代码可能很难理解,但我希望我能充分解释这些概念(源代码用于 CPython 实现,其他 Python 实现如 PyPy、IronPython 中的实现可能会有所不同!)。重要的外卖信息应该是 Python 在等式检查中不进行隐式转换,并且等式测试与真值测试完全无关。内置类型的实现几乎总是给出合理的结果:
- 所有数字类型都以某种方式实现相等(浮点数与整数比较,复数与整数和浮点数比较)
- 并且所有非零且非空的都是
truthy
.
但是,如果您创建自己的类,则可以根据需要覆盖相等性和真值测试(然后您会散布很多混乱)!
1在某些情况下,顺序会更改:
- 如果第二个操作数是第一个操作数的子类,则前两个步骤相反。
- 对于某些隐式相等检查
__eq__
,在调用任何方法之前检查对象身份。例如,当检查某个项目是否在列表中时,即1 in [1,2,3]
.