首先,代码:
>>> 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
的功能如下:
如果x为false,则y,否则x
我所知道的任何语言都不允许您这样做。那么,Python为什么这么做呢?
听起来像是将两个问题合并为一个问题。
首先是短路问题。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:
当CCD_ 14很容易成为CCD_。这个特殊的用例不是很有用,既因为更明确的x if x else 0
(在Python2.5及更高版本中)同样容易编写,也可能更容易理解(至少Guido是这么认为的),也因为None < 1
无论如何都与0 < 1
相同(至少在Python2.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站在同一侧。
我所知道的任何语言都不允许您这样做。那么,Python为什么这么做呢?
那么你不会很多语言。我想不出有哪种语言不表现出这种"短路"行为。
它这样做是因为说:很有用
a = b or K
使得如果b不是None(或以其他方式为falsy),则a变为b,如果不是,则获得默认值K。
实际上很多语言都这样做。请参阅维基百科关于短路评估
对于为什么存在短路评估的原因,维基百科写道:
如果用作条件的两个表达式都是简单的布尔变量,实际上,评估boolean中使用的两个条件会更快由于总是需要单个计算周期,与短路评估中使用的一个或两个循环相反(取决于第一个的值)。
这种行为并不令人惊讶,如果您认为Python具有以下关于或、和以及而不是逻辑运算符的功能,则它非常简单:
- 短路求值:它只在需要的地方求值操作数
-
非强制结果:结果是操作数之一,不强制为
bool
此外:
- 对象的真值仅适用于
None
、False
、0
、""
、[]
、{}
。其他所有东西的真值都为True(这是一种简化;正确的定义在官方文档中)
将这些功能结合起来,就会产生:
- 或:如果第一个操作数的求值结果为True,则将其短路并返回。或返回第二个操作数
- 和:如果第一个操作数的计算结果为False,则短路并返回。或返回第二个操作数
如果将其概括为操作链,则更容易理解:
>>> a or b or c or d
>>> a and b and c and d
以下是我记忆中的"经验法则",可以帮助我轻松预测结果:
- 或:返回它找到的第一个">truthy">操作数,或最后一个
- 和:返回它找到的第一个">falsy">操作数或最后一个操作数
至于你的问题,关于为什么python会这样做,好吧。。。我认为这是因为它有一些非常巧妙的用途,而且理解起来非常直观。常见的用法是一系列回退选择,使用第一个"找到"(即非falsy)。想想这个愚蠢的例子:
drink = getColdBeer() or pickNiceWine() or random.anySoda or "meh, water :/"
或者这个真实世界的场景:
username = cmdlineargs.username or configFile['username'] or DEFAULT_USERNAME
这比另一种更简洁和优雅。
正如许多其他答案所指出的,Python并不是唯一的,许多其他语言也有相同的行为,包括短路(我相信大多数当前语言都是)和非强制。
"我所知道的任何语言都不允许你这样做。那么,Python为什么要这样做呢?"你似乎认为所有语言都应该相同。难道你不希望编程语言的创新能够产生人们所重视的独特功能吗?
你刚刚指出了为什么它有用,那么Python为什么不这么做呢?也许你应该问问为什么其他语言没有。
您可以在布尔上下文之外利用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
)。