python“或”运算符奇怪的行为

时间:2012-12-13 23:28:16

标签: python operators boolean

首先,代码:

>>> 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呢?

6 个答案:

答案 0 :(得分:13)

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

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

其次,orand返回最后评估的值,而不是将其转换为bool。有两种方式可以争论,你可以在鸿沟的两边找到许多语言。

返回最后评估的值可以使用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认为如此),但也因为None < 1无论如何都与0 < 1相同(至少在Python 2.x中,所以你总是得到两个选项中的至少一个)...但是有类似的例子,很有用。比较这两个:

return launchMissiles() or -1

return launchMissiles() if launchMissiles() else -1

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


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

回到1.x天, 没有bool类型。您有None0[]()""等虚假值,其他一切都是真的,所以谁需要明确FalseTrue?从1返回or本来就很愚蠢,因为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相同的一面。

答案 1 :(得分:10)

  

我所知道的语言不允许你这样做。那么,为什么Python呢?

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

这样做是因为说:

很有用
a = b or K

如果b不是None(或其他假的),则a或者变为b,如果不是,则变为默认值K.

答案 2 :(得分:6)

实际上有很多语言都可以。请参阅Wikipedia关于Short-Circuit Evaluation

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

  

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

答案 3 :(得分:2)

这种行为并不令人惊讶,如果您认为Python具有以下关于以及的功能,那么这一点非常简单strong>逻辑运算符:

  • 短路评估:它只会根据需要评估操作数。
  • 非强制结果:结果是其中一个操作数,未强制转换为bool

另外:

  • 对象的真值仅适用于NoneFalse,{{1} False },0""[]。其他一切都有 True 的真值(这是一种简化;正确的定义在official docs

结合这些功能,它会导致:

  • :如果第一个操作数评估为 True ,则将其短路并返回。或者返回第二个操作数。
  • :如果第一个操作数评估为 False ,则将其短路并返回。或者返回第二个操作数。

如果您推广到一系列操作,这会更容易理解:

{}

这是&#34;经验法则&#34;我已经记住了,可以帮助我轻松预测结果:

  • :首先返回&#34; truthy &#34; 操作数,或者返回最后一个操作数。
  • :首先返回&#34; falsy &#34; 操作数,或者返回最后一个。

至于你的问题,在 为什么 python的行为就像那样,嗯...我认为因为它有一些非常巧妙的用途,而且相当不错直观易懂。一个常见的用途是一系列后备选择,第一个&#34;发现&#34; (即,非虚假)被使用。想想这个愚蠢的例子:

>>> a or b or c or d

>>> a and b and c and d

或者这个真实场景:

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

比其他选择更简洁更优雅。

正如许多其他答案所指出的那样,Python并不是唯一的,许多其他语言具有相同的行为,因为短路(我相信大多数当前语言都是)和非强制。< / p>

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

您可以在布尔上下文之外利用 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 操作数或最后一个操作数。