False == 0和True == 1是一种实现细节还是语言保证的?

295
在Python中,是否保证False == 0True == 1(假设它们没有被用户重新赋值)?例如,无论Python的版本(包括现有版本和可能的未来版本),下面的代码是否始终会产生相同的结果?
0 == False  # True
1 == True   # True
['zero', 'one'][False]  # is 'zero'

非常感谢任何提供官方文档的参考!
正如许多答案中所指出的,`bool`是从`int`继承而来。因此,可以将问题重新提出为:“官方文档是否明确表示程序员可以依赖布尔值从整数继承,且取值为`0`和`1`?”这个问题对于编写健壮的代码,避免因为实现细节而导致失败非常重要!

70
@S.Lott: 上述问题有许多原因需要询问。 因此,在某些情况下,依赖布尔值为整数可以使您的代码更简单:您必须更改它吗? 或者,您可能会发现其他人编写的代码中存在依赖于布尔值为整数的地方:您是否要在修改代码时打断正在修改的代码以“修复”现有代码,还是您可以放心当前的编码是正确的? 还有大量其他例子。 更一般地说,了解规则很重要,这样您就可以玩好它并以可靠的方式编程。 - Eric O. Lebigot
10
原帖确切地呼应了你的观点:问题本质上是“这是实现细节吗?”因为我完全同意你的想法,即一个人不应该依赖于实现细节。如果布尔值是已知值的官方整数,则问题中的代码不依赖于实现细节,这很好。 - Eric O. Lebigot
5
@S. Lot:知道 False==0 和 True==1 可以更容易地计算序列中有多少个布尔值为真:你只需要写 sum(bool_list)。否则,你就必须写成 sum(1 for x bool_list if x) - dan04
9
@dan:这是一种计算布尔值的方法。我会说bool_list.count(True)更加显式;而且速度也快大约3倍... :) - Eric O. Lebigot
3
正如答案所示,Python布尔值实际上是整数的一种特殊子类。此外,Python显然类型;也许你指的是它"不是静态类型"?同时,我不确定你所说的"I would not make errors in code"的含义。我从不喜欢将布尔值与整数混合使用,因为它们在概念上是不同的,如果Python布尔值不是整数,我也不介意,但知道它们是0和1的值是有用的。 - Eric O. Lebigot
显示剩余2条评论
4个回答

234
在Python 2.x中,并不保证这种情况,因为可能会重新分配TrueFalse。但即使发生这种情况,比较时仍会正确返回布尔值True和False。
在Python 3.x中,TrueFalse是关键字,并且始终等于10
在正常情况下,在Python 2中,以及始终在Python 3中: False对象的类型为bool,是int的子类:
    object
       |
     int
       |
     bool

这就是为什么在您的示例中,['zero','one'] [False] 起作用的唯一原因。它不会在不是整数子类的对象中起作用,因为列表索引仅适用于整数或定义了__index__方法的对象 (感谢mark-dickinson)。

编辑:

对于当前版本的Python和Python 3,这是正确的。 Python 2的文档Python 3的文档 都说:

有两种类型的整数:[...] 整数(int)[...] 布尔值(bool)

在布尔值小节中:

布尔值:表示False和True的真实值[...]布尔值在几乎所有情况下都像0和1的值一样,只有当转换为字符串时,分别返回字符串“False”或“True”。

还有 Python 2的文档:

在数字上下文(例如在用作算术运算符的参数时),它们[False和True]像整数0和1一样运行。

因此,在Python 2和3中,布尔值明确被视为整数。

所以在Python 4出现之前您都是安全的。;-)


3
0 == 0.0 返回True,而['zero','one'] [0.0] 失败。['zero','one'] [False] 正常工作,因为bool是int的子类。(int.subclasses()返回[<type 'bool'>]) - luc
25
细节说明:任何提供__index__方法的对象都可以用作列表索引,不仅仅是intlong的子类。 - Mark Dickinson
啊,是的,那里也有。但最好不要链接到Python 3.0文档:3.0已经过时了。 :) - Mark Dickinson
5
关于“在Python 2.x中,这并不能保证,因为True和False可能会被重新赋值”的说法,我认为虽然是事实,但任何重新赋值True或False的人应该自行承担奇怪后果。具体而言,在重新赋值之前将True存储起来,然后将结果与重新赋值后的True进行比较会导致错误。例如:a = True; True = 'i am an idiot'; a == True => False。除此之外,0和1作为默认值是标准化的,并且我相信依赖它们是一种常见做法,例如用它们作为索引到一个两个元素数组中,其中[0]代表false,[1]代表true。 - ToolmakerSteve
我刚刚注意到另一个官方确认的事实,即在实践中可以将True视为1,False视为0:https://docs.python.org/2/library/stdtypes.html#boolean-values。我将其添加到此答案中。 - Eric O. Lebigot
点赞是因为有一丝希望 Python 4 能解决这个问题。感谢您的贡献。 - Don Hatch

85

这里是有关Python 2.3中新的布尔类型的PEP讨论:http://www.python.org/dev/peps/pep-0285/

将布尔值转换为整数时,整数值始终为0或1,但将整数转换为布尔值时,除了0以外的所有整数都为True。

>>> int(False)
0
>>> int(True)
1
>>> bool(5)
True
>>> bool(-5)
True
>>> bool(0)
False

由于您提供了bool转换的示例,我想指出我刚刚遇到的一个问题,它导致我的代码出现了错误。bool('0')为True。 - Techniquab

23

在Python 2.x中,它根本不保证:

>>> False = 5
>>> 0 == False
False

所以它可能会改变。在Python 3.x中,True、False和None是保留字,所以上述代码将不起作用。

通常,在布尔值中,您应该假定False始终具有整数值0(只要您不像上面那样更改它),但True可以具有任何其他值。 我不会依赖于True==1的任何保证,但在Python 3.x中,无论如何,这都将始终成立。


3
“True” 可以是任何其他值。我不会仅凭 True==1 来保证任何内容。但实际上,根据 http://www.python.org/dev/peps/pep-0285/ 和 http://docs.python.org/2/reference/datamodel.html#the-standard-type-hierarchy ,你可以依赖于 True==1。其中提到:“在几乎所有情况下,布尔值的行为与分别为0和1的值相同……” 我并不是说在 Py 2 中无法通过重新赋值 True 或 False 来覆盖这一点,但我的意思是,除非你项目中的某个程序员很蠢并且做了这样的重新赋值,否则这种行为是被保证的。 - ToolmakerSteve
顺便说一下,有时候切换到Python风格会遇到问题。例如:if not (2 != 2): # <== 这是可以的 print ("Hello, world!") if ! (1 != 2): # <== SyntaxError print ("Hello, world!") ``` https://rextester.com/VSTV98648 - SL5net
当它说 0==FalseFalse 时,这是否真的意味着它是 5 - Max
这会将名称为“False”的变量与内置作用域中绑定到该名称的布尔类型值混淆。布尔值始终等于0,无论分配给该名称的值是什么。 - chepner

2
让我们把这个问题分成两部分。
Python:
print(45 == "45")  # Output - False

Javascript:

console.log(45 == "45")  # Output - true

在这里,大多数人认为JavaScript只检查两个对象的值,但事实并非如此。写console.log(45 == "45")print(45 == "45")是类似的。
但现在你有一个问题,如果两种语法相似,为什么我得到不同的结果呢?

在JavaScript中:

  • ==运算符在比较值之前执行类型转换。这意味着JavaScript会尝试将操作数转换为公共类型,然后再进行比较。
  • 在JavaScript中,对于45 == "45",字符串"45"会自动转换为数字,因为另一个操作数是数字。JavaScript尝试执行隐式类型转换以进行比较。字符串"45"可以转换为数字45,因此比较45 == "45"的结果为true
  • 类似于Python关键字is,JavaScript有一个===比较运算符。它检查两个对象是否位于同一台计算机的RAM内存位置。

在Python中:

在Python中,45"45"具有不同的类型。45<integer>类的一个实例,而"45"<string>类的一个实例。它们不是同一个类的实例。
与Javascript不同,Python不进行内部类型转换,所以print(45 == "45")的结果是False
Python使用内部类型转换,没错,但有一个条件。Python只会对boolean类型的对象进行内部类型转换。
因此,在表达式print(True == 1)中,布尔值True在执行比较之前被隐式地强制转换为整数1。结果是,比较print(1 == 1)得到的是True
当你写print(True == 1)时,等价于print(1 == 1)。当Python解释器在执行这行代码之前到达这行代码时,它会将print(True == 1)转换为print(int(True) == 1),最终变成print(1 == 1),根据==规则,两边的对象valuetype <class int>相同,因此你在终端看到的是True输出。
类似地,print(False == 0)等价于print(int(False) == 0),最终变成print(0 == 0),结果为True
点击这里查看。
# Normal behavior
sample_1 = True
print(sample_1) # Output - True
print(int(sample_1)) # Output - 1

sample_2 = False
print(sample_2) # Output - False
print(int(sample_2)) # Output - 0


# Try with True - boolean
A = True + 5  # equivalent to `A = int(True) + 5`
print(A) # Output - 6 (True ---> 1)


# Try with False - boolean
B = False + 5 # equivalent to `A = int(False) + 5`
print(B) # Output - 5 (False ---> 0)


# Try both boolean together
final = False + True # equivalent to `final = int(False) + int(True)`
print(final) # Output - 1  (False ---> 0 & True ---> 1)

希望能对大家有所帮助!

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