【问题标题】:What is the motivation for the "or" operator to not return a bool?“或”运算符不返回布尔值的动机是什么?
【发布时间】:2012-12-13 23:28:16
【问题描述】:

一、代码:

>>> False or 'hello'
'hello'

这种令人惊讶的行为让您可以在一行中检查 x 是否不是 None 并检查 x 的值:

>>> x = 10 if randint(0,2) == 1 else None
>>> (x or 0) > 0
# depend on x value...

解释:or 函数类似于this

如果 x 为假,则为 y,否则为 x

我知道的任何语言都不允许您这样做。那么,为什么是 Python?

【问题讨论】:

  • 你的意思是x or x > 0?这就是短路评估——很多语言都可以做到。
  • Javascript: val || default?
  • 真正的问题是为什么不应该这是 Python 的一个特性?为什么要假设你不能做某事?这有什么负面影响吗?
  • x or 0 > 0 不允许您依赖 x 值。事实上,它根本没有任何作用。也许你的意思是x = x or 0?但即便如此,这还是一件奇怪的事情——在上面的行中,它可以很容易地通过使用x = 10 if randint(0, 2)==1 else 0 而不是else None 来处理。所以……实际上,我不知道你的意图。
  • @SamMussmann 我的意思是:(无或 0)

标签: python operators boolean


【解决方案1】:

听起来您将两个问题合二为一。

首先,存在短路问题。 Marcin 的回答完美地解决了这个问题,所以我不会尝试做得更好。

其次,orand 返回最后评估的值,而不是将其转换为布尔值。两种方式都有争论,您可以在分歧的任何一方找到许多语言。

返回最后评估的值允许使用 functionCall(x) or defaultValue 快捷方式,避免可能的浪费转换(如果您要做的唯一事情,为什么要将 int 2 转换为 bool 1它是检查它是否非零?),通常更容易解释。因此,由于这些原因的各种组合,C、Lisp、Javascript、Lua、Perl、Ruby 和 VB 等语言都以这种方式做事,Python 也是如此。

始终从运算符返回布尔值有助于捕获一些错误(尤其是在逻辑运算符和位运算符容易混淆的语言中),它允许您设计一种语言,其中布尔检查是严格类型检查对于true 而不是仅检查非零,它使运算符的类型更容易写出,并且避免了在两个操作数是不同类型的情况下必须处理转换(请参阅C-中的?: 运算符)家庭语言)。因此,由于这些原因的各种组合,C++、Fortran、Smalltalk 和 Haskell 等语言都采用这种方式。


在您的问题中(如果我理解正确的话),您使用此功能能够编写如下内容:

if (x or 0) < 1:

x 很容易成为None。这个特殊的用例不是很有用,既因为更明确的x if x else 0(在 Python 2.5 及更高版本中)同样易于编写并且可能更容易理解(至少 Guido 是这么认为的),还因为@987654334 @ 无论如何都与0 &lt; 1 相同(至少在Python 2.x 中,所以你总是至少有两个选项之一)......但是也有类似的例子,它 很有用。比较这两个:

return launchMissiles() or -1

return launchMissiles() if launchMissiles() else -1

第二个将浪费大量导弹在南极洲炸毁你的敌人两次而不是一次。


如果你好奇 Python 为什么会这样:

在 1.x 时代,没有bool 类型。你有 None0[]()"" 等虚假值,其他一切都是真的,那么谁需要明确的 FalseTrue?从or 返回1 会很愚蠢,因为1 并不比[1, 2, 3]"dsfsdf" 更真实。在添加 bool 时(逐渐超过两个 2.x 版本,IIRC),当前的逻辑已经牢固地嵌入到语言中,并且更改会破坏很多代码。

那么,他们为什么不在 3.0 中改变它呢?许多 Python 用户,包括 BDFL Guido,建议您在这种情况下不应该使用or(至少因为它违反了“TOOWTDI”);您应该将表达式的结果存储在变量中,例如:

missiles = launchMissiles()
return missiles if missiles else -1

事实上,Guido 已经表示他想禁止launchMissiles() or -1,这也是他最终接受他之前多次拒绝的三元if-else 表达式的部分原因。但许多其他人不同意,Guido 是一个仁慈的 DFL。此外,让or 以您在其他任何地方所期望的方式工作,同时在这里拒绝做您想做的事情(但 Guido 不希望您做),实际上会非常复杂。


因此,在这里,Python 可能总是与 C、Perl 和 Lisp 站在同一边,而不是与 Java、Smalltalk 和 Haskell 站在同一边。

【讨论】:

  • 刚刚写了一个快速的fortran程序。 Gfortran(至少)短路。当我第一次阅读这个问题时,我不认为这与短路有关(+1)。 orand 不返回布尔值这一事实是我一开始使用 python 时真正让我感到惊讶的事情之一。不过过了一段时间,你就会习惯它(并学会用它来玩一些巧妙的技巧:)
  • @mgilson:gfortran 不是有 3000 个标志来控制它使用的 Fortran 77 方言吗?无论如何,我至少对 Fortran 返回布尔值是正确的吗?顺便说一句,我也很惊讶 Python orand 不返回 bool(或 10,因为还没有 bool),但原因不完全一样。我预计,如果有机会像 Smalltalk 和 C++,或者像 Perl 和 C,Python 会选择前者……但是一旦我意识到 1 并没有什么特别的(实际上,True 仍然不是) t),这是有道理的。
  • FWIW,Python 3.x 不允许 None &lt; 1(引发 TypeError)。
  • 从 3.8 开始可以return launched if (launched := launchMissiles()) else -1。这样发射导弹的数量就和return launchMissiles() or -1一样了
【解决方案2】:

没有我知道的语言可以让你这样做。那么,为什么 Python 会这样做呢?

那你不懂很多语言。我想不出一种我知道的语言不会表现出这种“短路”行为。

这样做是因为这样说很有用:

a = b or K

如果 b 不是 None(或其他错误),则 a 要么变为 b,否则它获取默认值 K。

【讨论】:

  • “我想不出一种我知道但不知道的语言”
  • @Marcin:Fortran、Pascal 和 Algol68 不会短路。 (好吧,许多 Pascal 会这样做,但 original 和 ISO 不会,Algol 为您提供了一种定义自己的短路运算符的方法。)此外,许多惰性语言在技术上不做短路,因为它们不需要到。但不知何故,我怀疑 OP 是否会从 Fortran 或 LazyML 转向 Python……
  • “我想不出一种我知道的语言不” c++、c# java、PHP 等它们都返回布尔值!再想一想。
  • @Marcin:我相信他说的是无法转换为 bool,而不是短路。
  • @Marcin:当然! x = [] or [1, 2, 3] 在 Python 中将 x 设置为 [1, 2, 3]。 C++ 或 Haskell 中的等价物将 x 设置为 True。这是一个巨大的差异。
【解决方案3】:

实际上,许多语言都可以。参见维基百科关于Short-Circuit Evaluation

出于为什么存在短路评估的原因,维基百科写道:

如果用作条件的两个表达式都是简单的布尔变量, 评估布尔值中使用的两个条件实际上可以更快 一次操作,因为它总是需要一个计算周期, 与短路评估中使用的一两个周期相反 (取决于第一个值)。

【讨论】:

  • 您的报价未提供存在短路评估的原因。
  • 我认为问题更多是关于返回第一个非假对象,而不是布尔值。 C 这样做; C++ 和 Java 没有。
【解决方案4】:

这种行为并不奇怪,如果您认为 Python 具有以下关于 orandnot 逻辑运算符的特性,那就很简单了:

  • 短路评估:它只评估操作数直到它需要的地方。
  • 非强制结果:结果是操作数之一,不强制为bool

另外:

  • 对象的真值,仅适用于NoneFalse0""[]{}。其他所有内容的真值都为 True这是一种简化;正确的定义在 official docs

结合这些特征,它会导致:

  • or :如果 first 操作数的计算结果为 True,则将其短路并返回。或者返回第二个操作数。
  • and:如果 first 操作数的计算结果为 False,则将其短路并返回。或者返回第二个操作数。

如果概括为一系列操作会更容易理解:

>>> a or b or c or d

>>> a and b and c and d

这是我记住的“经验法则”,可帮助我轻松预测结果:

  • or :返回它找到的第一个“truthy操作数,或者最后一个。
  • and:返回它找到的第一个“falsy操作数,或者最后一个。

至于你的问题,关于 为什么 python 的行为是这样的,嗯...我认为是因为它有一些非常简洁的用途,而且很容易理解。一个常见的用法是一系列后备选择,使用第一个“找到”(即,non-falsy)。想想这个愚蠢的例子:

drink = getColdBeer() or pickNiceWine() or random.anySoda or "meh, water :/"

或者这个真实世界的场景:

username = cmdlineargs.username or configFile['username'] or DEFAULT_USERNAME

这比替代方案更简洁优雅。

正如许多其他答案所指出的那样,Python 并不孤单,许多其他语言也具有相同的行为,无论是短路(我相信大多数当前语言都是)和非强制。

【讨论】:

  • 与这个答案相反,Python 没有 OR、AND 和 NOT 运算符。它有或,和没有。
  • @gwideman ,根据您的建议,我更改了大写字母和其他一些大小写/拼写改进
【解决方案5】:

“没有我知道的语言可以让你这样做。那么,为什么 Python 会这样做?”您似乎认为所有语言都应该相同。您不希望编程语言的创新能够产生人们重视的独特功能吗?

您刚刚指出了它为什么有用,那么 Python 为什么不这样做呢?也许你应该问为什么其他语言没有。

【讨论】:

    【解决方案6】:

    您可以在布尔上下文之外利用 Python 或运算符的特殊功能。经验法则仍然是布尔表达式的结果是第一个 true 操作数或行中的最后一个。

    请注意,逻辑运算符(或包含的)在赋值运算符 = 之前计算,因此您可以将布尔表达式的结果分配给变量,就像使用普通表达式一样:

    >>> a = 1
    >>> b = 2
    >>> var1 = a or b
    >>> var1
    1
    >>> a = None
    >>> b = 2
    >>> var2 = a or b
    >>> var2
    2
    >>> a = []
    >>> b = {}
    >>> var3 = a or b
    >>> var3
    {}
    

    这里,or 运算符按预期工作,返回第一个 true 操作数或最后一个操作数(如果两者都计算为 false)。

    【讨论】:

      猜你喜欢
      • 2013-02-27
      • 1970-01-01
      • 2011-07-22
      • 2015-02-19
      • 1970-01-01
      • 2020-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多