关于boolean:为什么’和’&’或’在Python中返回操作数?

Why do 'and' & 'or' return operands in Python?

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

我运行了以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> 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表示false,1True表示true。 (重要的例外:布尔运算orand总是返回其中一个操作数。)

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


我认为你对文档所说的内容感到困惑。看看这两个文档部分:真值测试和布尔运算符。引用第一节的最后一段:

Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)

正如您所看到的,您对操作和内置函数是正确的,但是请参阅重要的异常部分,可以说布尔运算符将返回其中一个操作数。

现在,他们可以返回的内容几乎不取决于运营商的短路逻辑。对于or运算符,它将返回表达式中的第一个truthy值,因为当它找到一个时,整个表达式为true。如果每个操作数都是假的,or将返回最后一个操作数,这意味着它遍历每个操作数都无法找到真正的操作数。

对于and运算符,如果表达式为true,则返回最后一个操作数,如果表达式为false,则返回第一个falsey操作数。您可以在维基百科页面上阅读有关短路评估的更多信息。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> 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'

我认为这应该说明一点。为了帮助您进一步了解其有用的原因,请考虑以下示例:

您有一个随机生成名称的函数

1
2
3
4
import random

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

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

1
2
if var is None:
    var = generate_name()

你可以做oneliner:

1
var = var or generate_name()

由于None是假值,or将继续搜索并评估第二个操作数,即调用该函数最终返回生成的名称。这是一个非常愚蠢的例子,我已经看到了这种风格的更好的用法(尽管不是Python)。我现在无法提出一个更好的例子。你也可以看一下这个问题,关于这个主题有非常有用的答案:Python是否支持短路?

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

希望这可以帮助!


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

使用and(记住True and X = X),右表达式的结果被推入堆栈,如果它是假的,它会立即被加速,否则第二个表达式被加速:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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
>>>

sm to:

1
2
3
4
def exec_and(obj1, obj2):
    if bool(obj1) != False:
        return obj2
    return obj1

使用or,如果第一个表达式为true,则会立即弹出。如果没有,第二个表达式是poped,现在结果真的取决于第二个。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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
>>>

sm to:

1
2
3
4
def exec_or(obj1, obj2):
    if bool(obj1) != True:
        return obj2
    return obj1


有人希望andor计算一个操作数(而不是总是评估为TrueFalse),以支持如下的习语:

1
2
3
4
5
6
7
8
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之类的值的真实性的代码来说,这是正确的。它偶尔但很少会导致误解。


考虑以下用例:

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

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

or的另一个用例

1
print element or 'Not found!'

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

这可以简化为

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

并且在功能上等同于:

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