Python 中的“布尔”运算(即:and/或运算符)

"Boolean" operations in Python (ie: the and/or operators)

提问人:NullUserException 提问时间:9/30/2010 更新时间:11/27/2014 访问量:6699

问:

此方法搜索第一组单词字符(即:),返回第一个匹配的组或失败。[a-zA-Z0-9_]None

def test(str):
    m = re.search(r'(\w+)', str)
    if m:
        return m.group(1)
    return None

相同的函数可以改写为:

def test2(str):
    m = re.search(r'(\w+)', str)
    return m and m.group(1)

这同样有效,并且是记录在案的行为;正如本页明确指出的:

表达式首先计算 ;如果为 false,则返回其值;否则,将进行计算并返回结果值。x and yxxy

但是,作为一个布尔运算符(它甚至在手册上这么说),我希望返回一个布尔值。结果,当我发现(如何)这是如何工作的时,我感到很惊讶。and

还有哪些其他用例,和/或这种相当不直观的实现的基本原理是什么?

python boolean-expression 最小惊讶

评论

1赞 bobince 9/30/2010
实际上,相当多的语言都有这个,它肯定早于 Python。传统上,它被用作三元条件的替代品,早在我们有表达式之前,它仍然可以像 C# 的 null-coalescing 运算符一样使用。就我个人而言,我会尽量避免使用它,除了小的琐碎用途,这既是因为“虚假”值的潜在问题,也是因为“显式比隐式好”。 差不多没问题,但如果你走得更远,可能会更清楚。if...else??return m and m.group(1)m is not None and...

答:

0赞 Glenn Maynard 9/30/2010 #1

我并不觉得这令人惊讶,事实上,当我最初尝试它时,我就希望它能起作用。

虽然并非所有值都是 ,但请注意,实际上,所有值都是布尔值 - 它们表示真值。(在 Python 中,is--in 效果 - 一个表示 true 或 false 的值。该数字不是布尔值,但它显式(在 Python 中)具有布尔值 False。boolsbool0

换句话说,布尔运算符并不总是返回一个 ,但它总是返回一个布尔值;一个表示真或假的,即使它还在逻辑上附加了其他信息(例如字符串)。andbool

也许这是追溯性的理由;我不确定,但无论哪种方式,Python 的布尔运算符的行为似乎都很自然。


何时使用?

在您的示例中,test2 对我来说更清晰。我能说出他们俩都做了什么:test2 中的结构并没有让它更难理解。在其他条件相同的情况下,test2 中更简洁的代码更容易理解。也就是说,这是一个微不足道的区别,我不喜欢任何一个,以至于我会跳起来重写任何东西。

它在其他方面也有类似的用处:

a = {
    "b": a and a.val,
    "c": b and b.val2,
    "d": c and c.val3,
}

这可以以不同的方式重写,但这是清晰、直接和简洁的。

不要过火;用“a()和b()或c()”代替“a()?b():c()“是危险和令人困惑的,因为如果 b() 是假的,你最终会得到 c()。如果你正在编写三元语句,请使用三元语法,即使它丑陋得可怕:.b() if a() else c()

评论

0赞 Russell Borogove 9/30/2010
我想说的是,如果读者不熟悉这个主题,test2 确实会更难理解。正在讨论这个话题的事实告诉我,明确的 if/else 更容易理解。
1赞 Glenn Maynard 9/30/2010
@Russell Borogove:对于任何给定的语法,都会有人不理解它,但更多的人正在讨论它。这在 Python(和其他几种语言;Python 没有发明这个),除非你正在为新手程序员的最小公分母编写教程代码,否则我认为这很好。
1赞 Jochen Ritzel 9/30/2010 #2

基本上返回与整个表达式具有相同真值的操作数。a and b

这听起来可能有点令人困惑,但只要在脑海中做:如果是,那么就不再重要了(因为永远是),所以它可以立即返回。aFalsebFalse and anythingFalsea

但是什么时候才重要,所以它甚至不用看就马上回来了。aTruebb

这是许多语言都会做的非常常见且非常基本的优化。

评论

0赞 NullUserException 9/30/2010
我知道短路评估,只是这个表达式可能具有完全不同的返回类型,这让我感到困扰。
1赞 S.Lott 9/30/2010
@NullUserException:一点也不“完全”不同。布尔等效值相同。
4赞 S.Lott 9/30/2010 #3

还有哪些其他用例,

不。

这种相当不直观的实现的基本原理是什么?

“不直观”?真?我不同意。

让我们想一想。

如果“A B”为假,则为伪造。因此,第一个错误值足以知道答案。为什么要费心转换为另一个布尔值?这已经是假的了。还有多少假?同样是错误的,对吧?aaFalse

所以 的值 -- 当等价于 -- 时,是足够假的,所以这就是整个表达式的值。无需进一步转换或处理。做。aFalse

当 的值等于 时,只需要 的值。无需进一步转换或处理。为什么要转换为另一个布尔值?它的价值是我们需要知道的。如果它像 ,那么它就足够真实了。还有多少真实?aTruebbTrueTrue

为什么要创建虚假的附加对象?

对 or 的分析相同。

为什么要转换为布尔值?它已经足够真实或足够虚假了。它还能得到多少真实?


试试这个。

>>> False and 0
False
>>> True and 0
0
>>> (True and 0) == False
True

而实际上,它等于 。对于所有实际目的来说,这都是错误的。(True and 0)0False

如果这是一个问题,那么将强制进行显式转换。bool(a and b)

6赞 Alex Martelli 9/30/2010 #4

还有哪些其他用例,

简洁(因此清晰,一旦你习惯了它,因为毕竟它根本不会牺牲可读性!-)任何时候你需要检查一些东西,如果它是真的,要么使用那个东西,或者如果那个东西是假的,则使用另一个值(那是为了--反转它--我非常刻意地避免使用实际的关键字-or-the-like和, 因为我说的是每个对象,而不仅仅是!andorTrueFalsebool

任何计算机屏幕上的垂直空间都是有限的,如果可以选择,最好将其花在有用的可读性辅助工具上(文档字符串、注释、战略性地放置空行以分隔块等),而不是转动,例如:

inverses = [x and 1.0/x for x in values]

分为六种,例如:

inverses = []
for x in values:
    if x:
        inverses.append(1.0/x)
    else:
        inverses.append(x)

或其更狭窄的版本。

和/或这样做的理由是什么 相当不直观的实现?

远非“不直观”,初学者经常被以下事实绊倒:某些语言(如标准帕斯卡)没有指定计算顺序和 和 的短路性质;Turbo Pascal 和语言标准之间的区别之一,在当时使 Turbo 成为有史以来最流行的 Pascal 方言,正是 Turbo 实现的,就像 Python 后来所做的那样(以及更早的 C 语言......andorandor

评论

1赞 NullUserException 9/30/2010
那不是应该的吗?x and 1.0/x for ...
0赞 Alex Martelli 9/30/2010
@Null,哎呀,你是对的——看,尽管我写了可怕的错别字,但你确实发现这个结构是可读的! Tx,编辑修复。
0赞 poke 9/30/2010
完全跑题了;是!-)笑脸吗?
0赞 S.Lott 9/30/2010
@poke:不,这是 Markdown 处理重音坟墓的产物。
1赞 user2357112 12/13/2018
不直观的部分不是评估顺序或短路;不直观的部分是布尔运算符返回非布尔值。知道在 Python 和被定义的时候,Python 实际上没有专用的布尔类型,这更有意义。andor
1赞 Cosmo F 11/27/2014 #5

我认为,虽然这种符号“有效”,但它代表了一种糟糕的编码风格,它隐藏了逻辑,并且会让更有经验的程序员感到困惑,他们将拥有大多数其他语言如何工作的“包袱”。

在大多数语言中,活动函数的返回值由函数的类型决定。除非它被明确重载。示例:“strlen”类型函数应返回整数而不是字符串。

诸如核心关节炎和逻辑函数(+-/*|&!)之类的在线函数更加受到限制,因为它们背后也有形式数学理论的历史。(想想所有关于这些函数的运算顺序的争论)

要让基本函数返回除其最常见的数据类型(逻辑或数字)之外的任何内容,应归类为有目的的混淆。

在几乎所有通用语言中,“&”或“&&”或“AND”都是一个逻辑或布尔函数。在幕后,优化编译器可能会在 LOGIC FLOW 中使用如上所述的短切逻辑,但不会使用 DATA STRUCTURE Modification(任何以这种方式更改值的优化编译器都会被视为损坏),但如果该值预期在变量中用于进一步处理,它应该是逻辑或布尔类型,因为在大多数情况下,这是这些运算符的“正式”。