为什么Python中的'and'和'or'返回操作数?

16 投票
4 回答
3270 浏览
提问于 2025-04-17 23:34

我在学习《通过艰难的方式学Python》(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 和 1 进行运算,你会得到第一个操作数(False),但如果你用 True 和 1,你会得到第二个操作数(1)。可以多试试这个。

4 个回答

1

考虑以下使用场景:

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

如果字典中存在 'foo' 这个键,就把 element 设置为 dict['foo'] 的值;如果不存在,就设置为 False。这个方法在写一个函数时很有用,可以返回一个值或者在失败时返回 False

再来看一个使用 or 的场景:

print element or 'Not found!'

把这两行代码放在一起,如果 'foo' 存在,就会打印出 dict['foo'] 的值;如果不存在,就会打印 'Not found!'(我使用 str() 是因为如果 element0False,那么 or 会失败,因为这两者被认为是“假”的,而我们只是打印出来,所以这没关系)。

这个可以简化为:

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

并且功能上等价于:

if dict.has_key('foo'):
    print dict['foo']
else:
    print 'Not found!'
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

人们希望 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 的值的代码中是普遍存在的。偶尔会导致一些误解,但并不常见。

16

我觉得你可能对文档的内容有些困惑。看看这两个文档部分:真值测试和布尔运算符。引用第一部分最后一段的话:

那些返回布尔结果的操作和内置函数,通常会返回0False表示假,返回1True表示真,除非另有说明。(重要例外:布尔运算符orand总是返回它们的某个操作数。)

如你所见,你说的操作和内置函数是对的,但注意重要例外部分,文中明确说明布尔运算符会返回它们的某个操作数

它们返回什么,主要取决于运算符的短路逻辑。对于or运算符,它会返回表达式中第一个为真的值,因为一旦找到一个,整个表达式就为真。如果所有操作数都是为假的,or会返回最后一个操作数,这意味着它遍历了每一个操作数,没能找到一个为真的。

对于and运算符,如果表达式为真,它会返回最后一个操作数;如果表达式为假,它会返回第一个为假的操作数。你可以在维基百科页面了解更多关于短路求值的信息

你在问题中有很多例子,我们来分析其中一些:

>>> 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()

你可以用一行代码来实现:

var = var or generate_name()

因为None是一个为假的值,or会继续查找并评估第二个操作数,也就是调用这个函数,最终返回生成的名字。这是一个很简单的例子,我见过更好的用法(虽然不是在Python中)这种风格。我现在想不出更好的例子。你也可以看看这个问题,里面有非常有用的答案:Python支持短路求值吗?

最后但同样重要的是,这与静态类型、鸭子类型、动态、解释型、编译型等语言没有关系。这只是语言的一个特性,可能会很有用,而且几乎每种我能想到的编程语言都提供这个特性。

希望这能帮到你!

撰写回答