Python:布尔运算符(和,或)不返回布尔值的理由

Python: justification for boolean operators (and, or) not returning booleans

[编辑:这个问题不是 的重复;我问的是语言设计的问题,即为什么首先会这样。我对如何它的行为不感到困惑,我对为什么首先决定这种行为感到困惑。]

在Python(和一些其他语言,例如Lua)中,布尔运算符andor不return布尔值TrueFalse 正如人们所期望的那样,而是:

很容易看出,在 xy 是布尔值的特殊情况下,它的行为符合预期。

我的问题是,以这种方式进行概括的实际理由是什么?我知道您可以将它用于以下技巧:

foo = x or None

或者像这样的 DIY 三元运算符:

foo = x and y or z

但我觉得这些似乎不足以为这种令人惊讶的行为提供充分的理由。每当我看到非布尔值 return 的实际值被依赖时(而不是在 if 语句或其他东西中,无论哪种方式都无关紧要),我总是必须仔细检查我对因为我总是忘记它是如何工作的,所以我需要再次查找它。我通常更喜欢以更冗长的方式编写它。是否有一个根本原因为什么它应该以这种方式工作,或者它真的只是为了技巧?

在 Python 的例子中,bool 类型直到该语言发布十多年后才出现。如果它从一开始就存在,也许 andor 的定义会有所不同。但是,照原样,向后兼容性要重要得多。来自 the PEP 介绍的类型:

Another consequence of the compatibility requirement is that the expression "True and 6" has the value 6, and similarly the expression "False or None" has the value None. The "and" and "or" operators are usefully defined to return the first argument that determines the outcome, and this won't change; in particular, they don't force the outcome to be a bool. Of course, if both arguments are bools, the outcome is always a bool. It can also easily be coerced into being a bool by writing for example "bool(x and y)".

编辑:顺便说一句,Python 最初的目的是“弥合”使用“数据丰富”语言(如 C)和“方便”语言(如 Unixy [=)编程之间的差距29=]s(shcsh 等)。 Python 的 andor 更像是 &&|| 使用 shell 语言,消费 - 从左到右 - 所有且仅确定整个链是成功(“真”)还是失败(“假”)所需的操作数。

我至少可以想到一个令人信服的理由:它允许您定义使用 andor 的自定义布尔类型。这是一个例子:

from dataclasses import dataclass


@dataclass
class CoolBool:
    value: bool
    note: str

    def __bool__(self):
        return self.value

    def answer(self):
        return "Yes" if self else "No"


print(CoolBool(False, "foo") or CoolBool(True, "bar"))
# -> CoolBool(value=True, note='bar')

也许字符串注释不是特别有用,但我认为能够在类似布尔值的 class 上定义方法可能是。

andor 是价值选择者而不是价值创造者。您可以使用运算符链的已解析值,而无需知道是哪个运算决定了结果。它有多有用?这是有争议的。选择默认值 value or default 和简单的 if-then-else condition and foo or bar(当然在三元运算符之前)很熟悉。

Python 实际上并不知道对象被解析后会发生什么。也许这将是一个测试 if foo or bar: 并且 bool() (或其 C 等价物)将被调用。也许它将被用作实例 (foo or bar).baz() 或将被添加到某些东西中 (val or 100) + 10。关键是,它决定了程序的下一步。当您不知道接下来会发生什么时,为什么要将其转换为布尔值?

由于临时解析的对象已经持有正在进行的操作的“真实性”,python 可以简单地将对象传递到下一个处理阶段,如此反汇编所示。当链完成并且 python 跳转到标记为“>>”的行时,正确的内容已经在寄存器中用于操作。

>>> def foo(a,b):
...     return (a or b or 100) + 10
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                0 (a)
              2 JUMP_IF_TRUE_OR_POP     10
              4 LOAD_FAST                1 (b)
              6 JUMP_IF_TRUE_OR_POP     10
              8 LOAD_CONST               1 (100)
        >>   10 LOAD_CONST               2 (10)
             12 BINARY_ADD
             14 RETURN_VALUE