【问题标题】:Why do 'and' & 'or' return operands in Python?为什么'and' & 'or' 在 Python 中返回操作数?
【发布时间】:2014-05-01 04:03:18
【问题描述】:

我正在经历 LPTHW,但遇到了一些我无法理解的事情。什么时候你希望你的布尔值 andor 返回布尔值以外的东西? LPTHW 文本指出,像 python 这样的所有语言都有这种行为。他是指解释型语言还是编译型语言,还是鸭子类型语言和静态类型语言?

我运行了以下代码:

>>> False and 1
False
>>> True and 1
1
>>> 1 and False
False
>>> 1 and True
True
>>> True and 121
121
>>> False or 1
1
>>> False or 112
112
>>> False or "Khadijah"
'Khadijah'
>>> True and 'Khadijah'
'Khadijah'
>>> False or 'b'
'b'
>>> b = (1, 2, "K")
>>> b
(1, 2, 'K')
>>> False or b
(1, 2, 'K')
>>> 

请帮助我了解这里发生了什么。

根据文档:http://docs.python.org/2/library/stdtypes.html

除非另有说明,否则具有布尔结果的操作和内置函数始终返回 0False 为假,1True 为真。 (重要的例外:布尔运算 orand 总是返回它们的一个操作数。)

根据 LPTHW:http://learnpythonthehardway.org/book/ex28.html 为什么"test" and "test" 返回 "test" 或 1 and 1 返回 1 而不是 True? Python 和许多类似的语言将操作数之一返回到其布尔表达式,而不仅仅是 True 或 False。这意味着,如果您执行 False and 1,您将获得第一个操作数 (False),但如果您执行 True and 1,您将获得第二个操作数 (1)。玩一下这个。

【问题讨论】:

  • 布尔运算也是惰性的。当找到第一个 False 操作数时,and 操作将停止计算操作数。 or 操作将在找到第一个 True 操作数时停止计算操作数。
  • 如果所有操作数都为 True,and 操作将返回最后一个操作数。 or 操作将返回第一个计算结果为 True 的操作数。有一些用例 - 记住这一点,有一天你会说啊哈!

标签: python boolean operands


【解决方案1】:

我认为您对文档所说的内容感到困惑。看看这两个文档部分:Truth Value Testing and Boolean Operators。引用第一部分的最后一段:

除非另有说明,否则具有布尔结果的操作和内置函数始终返回 0False 为 false 和 1True 为 true 。 (重要的例外:布尔运算orand 总是返回它们的一个操作数。)

如您所见,您对操作和 内置函数 是正确的,但请参阅 重要异常 部分,很好地说明了布尔运算符 将返回其中一个操作数

现在,它们能返回什么几乎不取决于操作员的短路逻辑。对于or 运算符,它将返回表达式中的第一个 truthy 值,因为当它找到一个时,整个表达式为 true。如果每个操作数都是 falseyor 将返回最后一个操作数,这意味着它遍历了每个操作数,但无法找到真实的操作数。

对于and运算符,如果表达式为真,则返回最后一个操作数,如果表达式为假,则返回第一个假操作数。你可以阅读更多关于Short Circuit Evaluation at the Wikipedia Page的信息。

你的问题有很多例子,我们来分析一下:

>>> False and 1  # return false (short circuited at first falsey value)
False
>>> True and 1   # return 1 (never short circuited and return the last truthy value)
1
>>> 1 and False  # return false (short circuited at first falsey value, in this case the last operand)
False
>>> 1 and True  # return True (never short circuited and return the last truthy value)
True
>>> True and 121  # return 121 (never short circuited and return the last truthy value)
121
>>> False or 1  # return 1 (since the first operand was falsey, or kept looking for a first truthy value which happened to be the last operator)
1
>>> False or 112  # return 112 for same reason as above
112
>>> False or "Khadijah"  # return "Khadijah" for same reason as above
'Khadijah'
>>> True and 'Khadijah'  # return "Khadijah" because same reason as second example
'Khadijah'

我认为这应该说明问题。为了帮助您进一步了解这为何有用,请考虑以下示例:

你有一个随机生成名字的函数

import random

def generate_name():
    return random.choice(['John', 'Carl', 'Tiffany'])

你有一个变量,你不知道它是否已经分配了一个名称,所以不要这样做:

if var is None:
    var = generate_name()

你可以做oneliner:

var = var or generate_name()

由于None 是一个虚假值,or 将继续搜索并评估第二个操作数,即调用最终返回生成名称的函数。这是一个非常愚蠢的例子,我见过这种风格的更好用法(虽然不是在 Python 中)。我现在想不出更好的例子。你也可以看看这个问题,关于这个话题有非常有用的答案:Does Python support short-circuiting?

最后但并非最不重要的一点是,这与静态类型、鸭子类型、动态、解释、编译等任何语言无关。这只是一种语言功能,可能会派上用场,而且很常见,因为我能想到的几乎所有编程语言都提供此功能。

希望这会有所帮助!

【讨论】:

  • 问题是为什么存在这种行为,我不相信你已经解释过了。
  • @KonradRudolph 我目前正在寻找一个有用的示例,说明如何使用短路运算符进行分配:)
  • @KonradRudolph 此外,OP 说:请帮助我了解这里发生了什么,根据文档,这不应该发生......所以我猜他/她正在寻找解释。
  • 我想我理解这与布尔值的“短路”性质有关。这个例子很好。我实际上看到了它的用途,我相信它稍后会出现在一些代码中。
【解决方案2】:

人们可能希望 andor 计算为一个操作数(而不是总是计算为 TrueFalse),以便支持如下惯用语:

def foo(self):
    # currentfoo might be None, in which case return the default
    return self.currentfoo or self.defaultfoo()

def foobar(self):
    # foo() might return None, in which case return None
    foo = self.foo()
    return foo and foo.bar()

您当然可以质疑此类成语的价值,尤其是在您不习惯它们的情况下。始终可以使用显式 if 编写等效代码。

作为反对他们的一个观点,他们怀疑是否有可能并有意考虑全部虚假值,或者只是评论中提到的那个(不允许其他虚假值)。但是,对于使用可能不是TrueFalse 的值的真实性的代码来说,这通常是正确的。偶尔但很少会导致误解。

【讨论】:

  • 在 lambdas 的情况下,这些习语确实是做某些事情的唯一方法。当然,你也可以争论 lambdas 的价值,所以... :)
  • @roippi:它们不是 only 方式,您可以为 lambda 编写另一个函数来调用 ;-)。但是,是的,无论好坏,lambdas 和其他承认单个表达式的上下文总是有点像一只手被绑在背后。因此,可以编写的等效代码最终可能会变得更加冗长,具体取决于更改所破坏的内容。
  • @roippi 在大多数情况下,true_result if expression else false_result 可能也很有用。但这比 and/or 的东西更新,and/or 有时更短。
【解决方案3】:

这与 Python 中实现短路效果的方式有关。

使用and(记住True and X = X),正确的表达式的结果被压入堆栈,如果为假,则立即弹出,否则弹出第二个表达式:

>>> import dis
>>> 
>>> dis.dis(lambda : True and 0)
  1           0 LOAD_CONST               2 (True)
              3 JUMP_IF_FALSE_OR_POP     9
              6 LOAD_CONST               1 (0)
        >>    9 RETURN_VALUE
>>>
>>> True and 0
0
>>> 0 and True
0
>>>

微笑:

def exec_and(obj1, obj2):
    if bool(obj1) != False:
        return obj2
    return obj1

对于or,如果第一个表达式为真,它会立即弹出。如果不是,则弹出第二个表达式,现在结果实际上取决于第二个。

>>> dis.dis(lambda : 0 or False)
  1           0 LOAD_CONST               1 (0)
              3 JUMP_IF_TRUE_OR_POP      9
              6 LOAD_CONST               2 (False)
        >>    9 RETURN_VALUE
>>>
>>> True or 0
True
>>> 1 or False
1
>>> False or 1
1
>>>

微笑:

def exec_or(obj1, obj2):
    if bool(obj1) != True:
        return obj2
    return obj1

【讨论】:

    【解决方案4】:

    考虑以下用例:

    element = dict.has_key('foo') and dict['foo']
    

    element 设置为dict['foo'](如果存在),否则设置为False。这在编写函数以返回值或 False 失败时很有用。

    or 的另一个用例

    print element or 'Not found!'
    

    将这两行放在一起会打印出dict['foo'](如果存在),否则会打印出'Not found!'(我使用str(),否则当element0(或False)时or 会失败) 因为这被认为是 falsey 并且因为我们只是打印它并不重要)

    这可以简化为

    print dict.has_key('foo') and str(dict['foo']) or 'Not found!'
    

    并且在功能上等价于:

    if dict.has_key('foo'):
        print dict['foo']
    else:
        print 'Not found!'
    

    【讨论】:

      猜你喜欢
      • 2013-10-11
      • 1970-01-01
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-24
      • 1970-01-01
      • 2013-02-19
      相关资源
      最近更新 更多