【问题标题】:"Boolean" operations in Python (ie: the and/or operators)Python 中的“布尔”运算(即:和/或运算符)
【发布时间】:2010-09-29 22:53:15
【问题描述】:

该方法搜索第一组单词字符(即:[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)

这也是一样的,并且是记录在案的行为;正如this page 明确指出的那样:

表达式x and y首先计算x;如果x 为假,则返回其值;否则,将评估 y 并返回结果值。

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

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

【问题讨论】:

  • 其实很多语言都有这个,它肯定早于Python。它传统上被用作三元条件的替代品,早在我们有if...else 表达式之前,它仍然可以像C# 的空合并运算符?? 一样使用。就我个人而言,我会尽量避免使用它,这既是因为“虚假”值的潜在问题,也是因为“显式优于隐式”。 return m and m.group(1) 还可以,但如果你走得更远,m is not None and... 可能会更清楚。

标签: python boolean-expression least-astonishment


【解决方案1】:

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

这听起来可能有点令人困惑,但只要在你的脑海中做:如果aFalse,那么b 不再重要(因为False and anything 将永远是False),所以它可以立即返回a

但是当aTrue 时,只有b 很重要,所以它会立即返回b,甚至不用看。

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

【讨论】:

  • 我知道短路评估,只是这个表达式有完全不同的返回类型的可能性让我感到困扰。
  • @NullUserException:根本没有“完全”不同。布尔等效值是一样的。
【解决方案2】:

还有什么其他用例,

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

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

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)

或更多狭窄的版本。

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

远非“不直观”,初学者经常被以下事实绊倒和or; Turbo Pascal 和语言标准之间的差异之一,这在当时使 Turbo 成为有史以来最流行的 Pascal 方言,正是 Turbo 实现了 andor,就像 Python 后来所做的一样(以及 C 语言之前做过...)。

【讨论】:

  • 不应该是x and 1.0/x for ...吗?
  • @Null,oops,你说得对——看,你确实发现这个结构是可读的,尽管我犯了可怕的错字!-) Tx,编辑修复。
  • 完全偏离主题;是!-) 笑脸?
  • @poke:不,这是 Markdown 处理重音坟墓的产物。
  • @poke,是的,它是一个笑脸,但带有眼罩。
【解决方案3】:

还有什么其他用例,

没有。

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

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

让我们想想。

如果a 为假,则

“a and b”是伪造的。所以第一个假值就足以知道答案了。为什么要麻烦将a 转换为另一个布尔值?已经是假的了。 False 又是多少错误?同样是假的,对吧?

所以a 的值——当等于False 时——是足够的假,所以这就是整个表达式的值。没有进一步的转换或处理。完毕。

a 的值等于True 时,b 的值就是所需要的。没有进一步的转换或处理。为什么要将b 转换为另一个布尔值?我们只需要知道它的价值。如果是True 之类的东西,那就够了。 True 更真实吗?

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

的分析相同。

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


试试这个。

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

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

如果有问题,则bool(a and b) 将强制进行显式转换。

【讨论】:

    【解决方案4】:

    我并不觉得这令人惊讶,事实上,当我最初尝试它时,我就预料到它会起作用。

    虽然并非所有值都是bools,但请注意,实际上,所有值都是布尔值——它们代表一个真值。 (在 Python 中,bool 是——实际上——一个代表真或假的值。)数字0 不是布尔值,但它是显式的(在 Python 中)布尔值为 False。

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

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


    什么时候使用?

    在您的示例中,test2 对我来说感觉更清晰。我可以告诉他们两者都做同样的事情:test2 中的构造并没有使它更难理解。在其他条件相同的情况下,test2 中的代码越简洁——在一定程度上——越容易被理解。也就是说,这是一个微不足道的区别,我不太喜欢我会跳到重写任何东西。

    它在其他方面也同样有用:

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

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

    不要过火; "a() and b() or c()" 作为 "a()? b():c()" 的替代是危险和令人困惑的,因为如果 b() 是,你最终会得到 c()错误的。如果您正在编写三元语句,请使用三元语法,即使它非常难看:b() if a() else c()

    【讨论】:

    • 我想说如果读者不熟悉这个主题,test2 确实会让人更难理解。正在讨论该主题的事实告诉我,明确的 if/else 更容易理解。
    • @Russell Borogove:对于任何给定的语法,总会有人不理解它,但有更多人在讨论它。这在 Python 中是基本的(以及其他几种语言;这不是 Python 发明的),除非您编写的教程代码是为新手程序员的最小公分母编写的,否则我认为这很好。
    【解决方案5】:

    我认为,虽然这种表示法“有效”,但它代表了一种糟糕的编码风格,它隐藏了逻辑,并且会使经验丰富的程序员感到困惑,他们将“包袱”了解大多数其他语言的工作原理。

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-09-27
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 2011-04-20
      • 1970-01-01
      相关资源
      最近更新 更多