python“或”运算符奇怪的行为
Posted
技术标签:
【中文标题】python“或”运算符奇怪的行为【英文标题】:What is the motivation for the "or" operator to not return a bool? 【发布时间】:2012-12-01 23:08:56 【问题描述】:一、代码:
>>> False or 'hello'
'hello'
这种令人惊讶的行为让您可以检查 x != None 并在一行中检查 x 值:
>>> x = 10 if randint(0,2)==1 else None
>>> (x or 0) > 0
depend on x value...
解释:“或”函数如下:(link) "如果 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)
【参考方案1】:
您可以在布尔上下文之外利用 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
)。
【讨论】:
【参考方案2】:这种行为并不奇怪,如果您认为 Python 具有以下关于 or、and 和 not 逻辑运算符的特性,那就很简单了:
短路评估:它只评估操作数直到它需要的地方。 非强制结果:结果是操作数之一,不强制为bool
。
另外:
对象的真值为假,仅适用于None
、False
、0
、""
、 []
,
。其他所有内容的真值都为 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 ,根据您的建议,我更改了大写字母和其他一些大小写/拼写改进【参考方案3】:听起来您将两个问题合二为一。
首先,存在短路问题。 Marcin 的回答完美地解决了这个问题,所以我不会尝试做得更好。
其次,or
和 and
返回最后评估的值,而不是将其转换为布尔值。两种方式都有争论,您可以在分歧的任何一方找到许多语言。
返回最后评估的值允许使用 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 < 1
相同(至少在Python 2.x 中,所以你总是至少有两个选项之一)......但是也有类似的例子,它 很有用。比较这两个:
return launchMissiles() or -1
return launchMissiles() if launchMissiles() else -1
第二个将浪费大量导弹在南极洲炸毁你的敌人两次而不是一次。
如果你好奇 Python 为什么会这样:
在 1.x 时代,没有bool
类型。你有 None
、0
、[]
、()
、""
等虚假值,其他一切都是真的,那么谁需要明确的 False
和 True
?从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)。or
和 and
不返回布尔值这一事实是我一开始使用 python 时真正让我感到惊讶的事情之一。不过过了一段时间,你就会习惯它(并学会用它来玩一些巧妙的技巧:)
@mgilson:gfortran
不是有 3000 个标志来控制它使用的 Fortran 77 方言吗?无论如何,我至少对 Fortran 返回布尔值是正确的吗?顺便说一句,我也很惊讶 Python or
和 and
不返回 bool(或 1
和 0
,因为还没有 bool
),但原因不完全一样。我预计,如果有机会像 Smalltalk 和 C++,或者像 Perl 和 C,Python 会选择前者……但是一旦我意识到 1
并没有什么特别之处(实际上,True
仍然不是t),这是有道理的。
FWIW,Python 3.x 不允许 None < 1
(引发 TypeError
)。
从 3.8 开始可以return launched if (launched := launchMissiles()) else -1
。这样发射导弹的数量就和return launchMissiles() or -1
一样了【参考方案4】:
“没有我知道的语言可以让你这样做。那么,为什么 Python 会这样做?”您似乎认为所有语言都应该相同。您不希望编程语言的创新能够产生人们重视的独特功能吗?
您刚刚指出了它为什么有用,那么 Python 为什么不这样做呢?也许你应该问为什么其他语言没有。
【讨论】:
【参考方案5】:没有我知道的语言可以让你这样做。那么,为什么 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
。这是一个巨大的差异。【参考方案6】:
实际上,许多语言都可以。参见***关于Short-Circuit Evaluation
出于为什么存在短路评估的原因,***写道:
如果用作条件的两个表达式都是简单的布尔变量, 评估布尔值中使用的两个条件实际上可以更快 一次操作,因为它总是需要一个计算周期, 与短路评估中使用的一两个周期相反 (取决于第一个值)。
【讨论】:
您的报价未提供存在短路评估的原因。 我认为问题更多是关于返回第一个非假对象,而不是布尔值。 C 这样做; C++ 和 Java 没有。以上是关于python“或”运算符奇怪的行为的主要内容,如果未能解决你的问题,请参考以下文章