使用布尔值作为整数在Python中是否符合Pythonic风格?

71

False等同于0True等同于1,因此可以像这样进行操作:

def bool_to_str(value):
    """value should be a bool"""
    return ['No', 'Yes'][value]

bool_to_str(True)

注意值为bool但被用作int的情况。

这种用法是否符合Python风格或应该避免使用?


1
这本质上是 https://dev59.com/y3E85IYBdhLWcg3wdDSz 的副本,其中的答案也很有趣! - Eric O. Lebigot
7个回答

176

我将成为不同寻常的声音(因为所有答案都在谴责使用False == 0True == 1这一事实,因为语言保证),我认为利用这个事实来简化您的代码是完全可以的。

从历史上看,逻辑真假操作通常只使用0表示false,用1表示true。在Python 2.2的生命周期内,Guido注意到许多模块都以诸如 false = 0; true = 1 这样的赋值开始,这产生了样板和无用的变化(后者是因为真和假的大写形式随处可见--有些使用全部大写,有些全部小写,有些以大写字母开头)因此引入了bool子类int以及它的True和False常量。

当时确实有一些反对意见,因为我们中的许多人担心新类型和常数会被Python新手用来限制语言的能力, 但Guido坚信我们只是太悲观了:例如,没有人会如此糟糕地理解Python,以至于避免使用FalseTrue作为列表索引或求和等完全清晰和有用的习语。

这个主题的答案证明了我们的正确性:正如我们所担心的那样,对这种类型和常数角色的完全误解已经出现了,并且人们正在避免,并且更糟糕的是,敦促其他人避免使用完全自然的Python结构,而改为无用的曲折。

在对抗这种误解的潮流时,我敦促每个人将Python作为Python使用,而不是试图将其强行塑造成其他语言的模式,因为这些语言的功能和首选风格与Python相当不同。在Python中,True和False在99.9%的情况下都像1和0一样,唯一的区别在于它们的str(...)(以及由此产生的repr(...))形式——就像索引、算术、位运算等操作一样,请放心使用。


9
与Ruby相比,如果您需要将布尔值视为整数,则需要使用val?1:0等类似的技巧性代码,而这种写法非常麻烦。 - John La Rooy
32
没有人会以如此糟糕的方式理解Python,例如避免将False和True作为列表索引的完全自然用法。我当然不反对以这种方式使用它们,但我绝不认为人们使用布尔类型来索引列表是“自然”的,除非他们恰好知道bool是int的子类。 - Michael Mrozek
4
真是有趣的历史课程!然而,我认为大多数人能够区分布尔值和整数(我也是如此...)。从某种意义上说,人们正在定义“使用Python作为Python”的含义:我们大多数人确实感到这两种类型之间存在逻辑上的区别(数学上也是如此:逻辑并不需要算术)。我很高兴Python允许我们用这种方式思考。 - Eric O. Lebigot
正如Dahlia的回答所示,与“…if…else…”方法相比,“索引”方法是不必要地限制性的。 - Eric O. Lebigot
2
我简直不敢相信我和Alex意见不合,但事实就是如此。在这种情况下使用布尔值作为索引可以避免鸭子类型,而'True' if value else 'False'则适用于任何真值value - mcepl

158

我同意Alex的看法。False==0True==1,这是没有问题的。

不过,在Python 2.5及以后的版本中,我会使用Python的条件表达式来回答这个问题:

def bool_to_str(value):
  return 'Yes' if value else 'No'

这样就不需要实际传递一个布尔值作为参数了,就像if x: ...接受任何类型的x一样,当传递None、字符串、列表或3.14时,bool_to_str()函数应该正确处理。


41

当然:

def bool_to_str(value):
    "value should be a bool"
    return 'Yes' if value else 'No'
更易读。

2
我不太同意这个观点...是的,使用 x if foo else y 这种方式更符合 Python 的风格,但我仍然认为它看起来很丑,并且经常会使代码变得臃肿。我认为问题中的代码看起来更清晰,虽然对于不了解隐式布尔转换的人可能会感到困惑,但是 x if foo else y 对于那些来自 foo ? x : y 世界的人来说同样令人困惑。尽管我必须承认,对于正在学习他们的第一门语言的人来说,x if foo else y 可能是最清晰的一种写法。 - Ivo Wetzel
2
@Ivo,我赞同您的实质意见,但我不同意“三目运算符”构造(条件在中间而不是像C语言那样在开头)对新手来说特别自然或清晰(在许多情况下它是较小的恶之一-这是另一回事;-)。 - Alex Martelli
不确定,看起来我也在大约同一时间得到了一个(也没有注释!)。 - Alex Martelli
1
快点,@AlexMartelli,Python的“三元”构造如此自然,以至于它可以被直接读作纯英语并且即使是非程序员也能理解。"如果为真则返回yes,否则返回no"。这是新手的优雅和自然语言的祝福。 - MestreLion

13

你的代码在某些情况下似乎不准确:

>>> def bool_to_str(value):
...     """value should be a bool"""
...     return ['No', 'Yes'][value]
...
>>> bool_to_str(-2)
'No'

我建议您仅使用条件运算符以提高可读性:

def bool_to_str(value):
    """value should be a bool"""
    return "Yes" if value else "No"

19
函数名为bool_to_str,带有文档注释value should be a bool。当您将参数-2传递给它时,您会惊讶地发现它返回了错误的答案 :) - Michael Mrozek
另一方面,我认为通常假定非0表示True... Dahlia的例子表明,...if...else...方法使您能够生成不必要受限制的代码。 - Eric O. Lebigot
4
虽然我基本上同意Alex Martelli的回答,但在这种情况下,利用... if ... else ...所提供的隐式转换为bool似乎更符合Python的风格。然后该函数适用于布尔值和其他任何类型。尽管也许不是最好的示例,因为我并不反对使用布尔值作为整数。 - anton.burger
如果您不确定接收到的值是严格的布尔值(即 TrueFalse)还是“真实表达式”(例如 a and b or c),那么只需将该值应用于 bool 函数。简单易行! - holdenweb

5
实际上,False == 0 和 True == 1 是语言的一个特性(它不依赖于实现):Python中 False == 0 和 True == 1 是一种实现细节还是语言保证? 然而,我同意大多数其他答案的观点:有更易读的方式来获得与 ['No', 'Yes'][value] 相同的结果,可以使用 … if value else … 或字典,它们分别具有提示和说明 value 是布尔值的优点。
此外,… if value else … 遵循非 0 即为 True 的惯例:即使在 value == -2 (value 为 True) 的情况下,它也能正常工作,正如 dahlia 所暗示的那样。在这种情况下,列表和字典方法不够健壮,因此我不建议使用它们。

1

使用布尔值作为整数是可以的,因为布尔值是整数的子类。

>>> isinstance(True, int)
True
>>> isinstance(False, int)
True

关于你的代码:把它放在一个单行函数里有点过头了。读者需要找到你的函数源码或文档并阅读它(函数名称并不能告诉你太多)。这会打断流程。只需将其内联,不要使用列表(在运行时构建),而是使用元组(如果值是常量,则在编译时构建)。例如:
print foo, bar, num_things, ("OK", "Too many!)[num_things > max_things]

0

个人认为这取决于你想如何使用这个事实,以下是两个例子

  1. 只需将布尔值用作条件语句即可。人们经常这样做。

    a = 0
    if a:
        do something
    
  2. 但是,如果你想计算成功的项目数量,代码可能不太容易让其他人阅读。

    def succeed(val):
        if do_something(val):
            return True
        else:
            return False
    
    count = 0
    values = [some values to process]
    for val in values:
        count += succeed(val)
    

但我确实看到生产代码是这样的。

all_successful = all([succeed(val) for val in values])
at_least_one_successful = any([succeed(val) for val in values])
total_number_of_successful = sum([succeed(val) for val in values])

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