【问题标题】:How does the Python conditional operator workaround work?Python 条件运算符解决方法如何工作?
【发布时间】:2010-12-29 03:44:21
【问题描述】:

根据我的阅读,我发现内置的三元运算符不存在(我很乐意了解更多信息。)。

我找到了以下代码作为替代:

def val():
    var = float(raw_input("Age:"))
    status = ("Working","Retired")[var>65]
    print "You should be:",status

我无法理解这段代码是如何工作的;谁能解释一下代码实际上是如何工作的?我也很想知道为什么三元运算符不存在;任何有关此的参考或链接都会很有用。

我在 Windows Vista 上运行 Python 2.6.4。

【问题讨论】:

  • Python 2.5 版本增加了三元运算符
  • -1:请不要称其为“THE”三元运算符。它只是一个条件运算符,它恰好是(许多)三元运算之一。其他三元运算包括pow(x,y,z)
  • 也许我错了,但通常运算符是“+”或“%”或(在这种情况下)“if”。即:不是函数。我不认为 pow 会被视为三元运算符,它只是一个具有三个输入的函数。 (三元运算符,是的。operator,否。)在许多语言中,像 this 这样的内联条件是唯一需要三个输入的运算符,因此是三元运算符(对于该语言),所以它确实不正确。维基救援! en.wikipedia.org/wiki/Ternary_operator
  • (x, y)[bool] 构造是 2.5 之前的标准习惯用法。与y if bool else x相比,它的缺点是总是计算xy,而三元运算符会将其中一项短路。
  • @Toji:“操作”(我使用的词)和“操作员”的区别对我来说太微妙了。许多语言在许多符号中都有许多运算符。一元和二元运算符/操作很常见。但是将这个(并且只有这个)运算符/操作称为“三元”是愚蠢的。更高元的运算符呢?这是一个糟糕的术语,并且可以通过将其称为条件运算符来轻松避免,因为它就是这样。它恰好是三元的(或具有 3 的元数),但这不是定义特征。

标签: python indexing boolean conditional-operator


【解决方案1】:

Python 的结构有点像 C 中的三元运算符等。它的工作原理是这样的:

my_var = "Retired" if age > 65 else "Working"

并且等价于这个 C 代码:

my_var = age > 65 ? "Retired" : "Working";

至于您发布的代码是如何工作的,让我们逐步介绍一下:

("Working","Retired")

创建一个 2 元组(一个不可变列表),索引 0 为元素“Working”,索引 1 为“Retired”。

var>65

如果 var 大于 65,则返回 True,否则返回 False。应用于索引时,它会转换为 1 (True) 或 0 (False)。因此,这个布尔值提供了在同一行创建的元组的索引。

为什么 Python 不是一直都有三元运算符?简单的答案是 Python 的作者 Guido van Rossum 不喜欢/不想要它,显然认为它是一种不必要的构造,可能导致混淆代码(以及任何在C 可能会同意)。但是对于 Python 2.5,他心软了,添加了上面看到的语法。

【讨论】:

  • 我记得坐在 OSCON keynote 中,Guido 谈到了他没有将三元运算符放入 Python 的原因。想一想,大量不同的开发人员和技术人员以及演讲者正在谈论 30 年前的旧新闻并且没有实现的语言特定功能的微小细节。没有试图提出更广泛的观点。我经历过的最无聊的主题演讲。
【解决方案2】:

Python(2.5 及更高版本)确实有您正在寻找的语法:

x = foo if condition else bar

如果condition 为True,则x 将设置为foo,否则将设置为bar

例子:

>>> age = 68
>>> x = 'Retired' if age > 65 else 'Working'
>>> x
'Retired'
>>> age = 35
>>> y = 'Retired' if age > 65 else 'Working'
>>> y
'Working'

【讨论】:

  • 请注意,这仅在 2.5 或更高版本中可用。旧版本中没有类似的东西。
  • @Pace 是的,尽管提问者提到他们使用的是 2.6.4。只是为了清楚起见添加了一条注释。
【解决方案3】:

因为 True 强制转换为 1 而 False 强制强制转换为 0 所以如果 var = 70

("Working","Retired")[var>65]

变成

("Working", "Retired")[1]

一个不错的小捷径......但我发现除了简单的条件之外,它可能会让人有点困惑,所以我会接受 TM 的建议

"Retired" if var > 65 else "Working"

【讨论】:

    【解决方案4】:

    索引到列表中

    使用

    [expression_when_false, expression_when_true][condition] # or
    (expression_when_false, expression_when_true)[condition]
    

    利用了在 Python 中 True 等于(但不是!)1 和 False 等于(但不是!)0 的事实。上面的表达式构造了一个包含两个元素的列表,并使用了 condition 在列表中建立索引并仅返回一个表达式。这种方法的缺点是两个表达式都被计算了。

    和-或快捷方式

    自从 Python 创建以来,就有了这种操作的一种形式:

    condition and expression_when_true or expression_when_false
    

    这采用了一条捷径并且只计算一个表达式,但有一个容易出错的缺点:expression_when_true 不得计算为非真值,否则结果是 expression_when_falseandor 在 Python 中是“短路”,适用以下规则:

    a and b #→ a if a is false, else b
    a or b  #→ a if a is true, else b
    

    如果 condition 为 false,则永远不会评估 expression_when_true,结果为 expression_when_false。 OTOH,如果 condition 为真,则结果为 (expression_when_trueexpression_when_false) 的结果;请参阅上表。

    三元条件运算符

    当然,从 Python 2.5 开始,就有了三元条件运算符:

    expression_when_true if condition else expression_when_false
    

    操作数的奇怪顺序(如果您习惯于类似 C 的三元条件运算符)归因于 to many things;总体意图是 condition 在大多数情况下都应该为真,以便最常见的输出首先出现并且最明显。

    【讨论】:

      【解决方案5】:

      短路布尔表达式

      还有一个短路逻辑操作的选项:

      >>> (2+2 == 4) and "Yes" or "No"
      'Yes'
      >>> (2+2 == 5) and "Yes" or "No"
      'No'
      

      在你的例子中:

      >>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
      Age: 20
      'Working'
      >>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
      Age: 70
      'Retired'
      

      Charming Python: Functional Programming in Python, Part 1 中了解有关此技术的更多信息。

      【讨论】:

        【解决方案6】:

        在您发布的代码中,以下行模拟三元:

        status = ("Working","Retired")[var>65]
        

        这里的元组("Working","Retired") 使用索引[var>65] 访问,其计算结果为True (1) 或False (0)。当使用索引0 访问它时,status 将是'Working';如果索引是1,那么它将被“退休”。这是进行条件赋值的一种相当晦涩的方法,如前所述,使用 py2.5 中引入的正常三元语法。

        【讨论】:

          【解决方案7】:

          最初没有三元运算符,因为“显式优于隐式”,因此被视为非pythonic。我也不太喜欢python的三元运算,但它确实存在:

          x = foo if condition else bar
          

          如 TM 所示。

          至于status = ("Working","Retired")[var>65]var > 65 返回一个布尔值:True 或 False;但是,Python 对布尔类型的处理非常弱:在某些情况下,True1False0。你可以通过>>> True == 1查看。

          【讨论】:

            【解决方案8】:
            status = ("Working","Retired")[var>65]
            

            这一行作为三元运算符工作,因为表达式 var>65 返回 1 或 0,具体取决于 var 是否大于 65。所以如果var>65,那么这一行就变成了这样:

            status = ("Working","Retired")[1]
            

            即序列("Working","Retired")的第二个元素。它看起来很奇怪,但如果你这样写的话就不奇怪了:

            status_sequence = ("Working","Retired")
            status = status_sequence[1]
            

            所以status = "Retired".

            同样,如果var<=65 则变为

            status = ("Working","Retired")[0]
            

            status = "Working"

            【讨论】:

              【解决方案9】:

              只有该代码的“status =”行实现了类似于三元运算符的功能。

              status = ("Working","Retired")[var>65]
              

              这将创建一个双元素元组,其中字符串“Working”在索引 0 处,“Retired”在索引 1 处。在此之后,它使用表达式的结果对该元组进行索引以选择两个项目之一var > 65.

              如果 var 的值大于 65,此表达式将返回 True(相当于 1,因此选择 'Retired')。否则它将返回 False(相当于 0,因此选择 'Working')。

              不过,这种方法与三元运算符之间有一个关键区别,尽管在您的特定示例中并不重要。使用元组索引方法,两个值都被评估但只返回一个。使用三元运算符,实际上只计算两个值中的一个;这被称为“短路”行为。在这种情况下可能很重要:

              status = funcA() if var > 65 else funcB()
              status = (funcB(), funcA())[var > 65]
              

              在第一种情况下,要么调用 funcA(),要么调用 funcB(),但绝不会同时调用两者。在后一种情况下,首先调用两者,并将结果存储在元组中——然后只选择一个,然后丢弃元组。

              了解 funcA() 或 funcB() 是否具有“副作用”尤其重要,这意味着它们在执行时会更改其他数据。

              【讨论】:

                【解决方案10】:

                在 Python 2.6 及更高版本中:

                print "You should be {0}.".format("retired" if var>65 else "working")
                

                在 Python 3.1 及更高版本中:

                print ("You should be {}.".format("retired" if var>65 else "working"))
                

                【讨论】:

                • 在您的第一个示例中,Python 2.5 没有这种字符串格式。
                • 啊,谢谢;我从佩斯的评论中获取了这个信息到另一个答案 - 你是对的,.format() 是在 2.6 中引入的。
                【解决方案11】:

                这是带有python三元运算符的形式

                def val():
                    var = float(raw_input("Age:"))
                    status = "Retired" if var > 65 else "Working"
                    print "You should be:",status
                

                您展示的代码有点棘手:它创建了一个两个元素的元组,其元素位于位置 0 和 1。要选择正确的元素,它使用返回布尔值的条件,但 python 中的布尔值是整数,因此您可以使用它作为 special 索引(它们可以是 0 或 1)。

                【讨论】:

                • 糟糕。你的三元条件是相反的。一个很好的例子,说明为什么您会选择使用其他结构来清楚说明。
                • 不幸的是,像这样简单的布尔逻辑总是很容易在不经意间翻转,无论其格式如何。我不认为三元真的和它有任何关系。
                • jmanning2k:我将答案恢复为初始形式,因为它已经正确。把它想象成一个英文短语:“如果你超过 65 岁,你应该退休,否则工作。”
                【解决方案12】:

                尝试根据此处给出的答案给出完整的答案。

                你找到的方式(请不要使用这个,因为它不太可读):

                def val():
                    var = float(raw_input("Age:"))
                    status = ("Working","Retired")[var>65]
                    print "You should be:",status
                

                使用 python 2.5+ 语法:

                def val():
                    var = float(raw_input("Age:"))
                    status = "Working" if var>65 else "Retired"
                    print "You should be:",status
                

                使用其他一些人仍然喜欢的常用方法:

                def val():
                    var = float(raw_input("Age:"))
                    status = var>65 and "Working" or "Retired"
                    print "You should be:",status
                

                我个人倾向于使用最后一个,因为操作数的顺序与 C 三元运算符相同。

                编辑: 发现最后一种方法存在一些问题(感谢 Roberto Bonvallet)。
                来自维基百科:

                如果 op1 可以,则此代码将中断 一个“假”值(无、假、0、 空序列或集合,...)作为 该表达式将返回 op2 (无论是真的还是假的) 而不是(假的)op1

                所以我的最终建议是使用 2.5+ 三元运算符,因为它简单、易读并且提供短路行为。

                【讨论】:

                • 我不敢相信人们一直在建议 and-or hack。它们是不等价的,因为 and-or 技巧有一些令人讨厌的极端情况,它不能按预期工作。 (我不是因为你是为了完整而回答你,而是为了完整而发表评论,以警告你不要这样做。)
                • and-or hack 有一个唯一的优势,那就是它允许短路行为。虽然使用它的风险通常太大(最大的是程序员不理解它,第二大的是下一个程序员不会理解它)。
                • 此答案中的第二个和第三个示例交换了“工作”和“退休”,这给出了错误的答案。试试这个:status = "Retired" if var>65 else "Working".
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-11-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-09-23
                • 2013-12-20
                • 1970-01-01
                相关资源
                最近更新 更多