Menu

Python and、or、not:逻辑运算符、短路求值与真值

Python 的 and、or 和 not 运算符如何工作:真值表、真值与假值、短路求值、为什么 and/or 返回其中一个操作数、优先级,以及 and/or 与 & 和 | 的区别。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

用三个单词代替符号

Python 的逻辑运算符是英文单词 and、or 和 not。其他语言写作 &&、|| 和 !。在 Python 中 && 和 || 会引发 SyntaxError,而 ! 只出现在 !=(“不等于”)里。

对 True 和 False 来说,规则和你预期的一样:

aba and ba or bnot a
TrueTrueTrueTrueFalse
TrueFalseFalseTrueFalse
FalseTrueFalseTrueTrue
FalseFalseFalseFalseTrue

and 要求两边都为真。or 至少需要一边为真。not 只接收一个值,并把它取反。

在实际代码中,两边通常是比较表达式,结果交给 if 或 while 使用:

输出:

eligible
standard ticket

对大多数条件来说,这些就够用了。本页其余部分介绍这些运算符遇到不是 True 或 False 的值时会做什么,以及决定表达式哪一边会执行的规则。

真值与假值

and、or 和 not 可以接收任何值,而不只是布尔值。Python 根据值的真值性来判断它算不算真。以下这些值是假值:

  • False 和 None
  • 任何数字类型的零:0、0.0
  • 空容器和空字符串:""、[]、()、{}、set()、range(0)

其他一切都是真值,包括 "0"、"False"、[0] 和 -1。你可以用 bool() 查看 Python 如何判断一个值:

输出:

0 False
1 True
-1 True
'' False
'0' True
[] False
[0] True
None False
0.0 False

字符串 "0" 是真值,因为它是非空字符串。从输入读取数字、在转换之前就做判断的人常会在这里踩坑。数字与布尔值页面介绍了 bool 与 int 的关系。

and 和 or 返回的是操作数

这正是 Python 与大多数语言不同的地方。and 和 or 不会产生新的 True 或 False,而是返回你给它们的两个值之一:

  • 如果 x 是真值,x or y 返回 x,否则返回 y。
  • 如果 x 是假值,x and y 返回 x,否则返回 y。

当两个操作数都是布尔值时,“返回操作数”和“返回布尔值”是一回事,所以直到你混入其他类型,才会注意到这个区别。

not 是例外:它总是返回 True 或 False。

如果你需要从 and/or 表达式得到真正的布尔值,比如要存起来,或者要从一个承诺返回 bool 的函数中返回,就用 bool() 包起来:

短路求值

Python 从左到右求值 and 和 or,一旦能确定结果就停下:

  • and 在遇到第一个假值时停下,因为后面的任何东西都不可能让整个表达式为真。
  • or 在遇到第一个真值时停下,因为后面的任何东西都不可能让整个表达式为假。

被跳过的那一边根本不会执行。用调用时会打印信息的函数,就能亲眼看到这一点:

输出:

and:
  checking A
False
or:
  checking A
True

两种情况下 B 都没有被检查。短路求值让守卫条件变得安全。下面每个条件的后半部分单独执行都会崩溃,但它只会在前半部分排除了危险情况之后才执行:

把开销小或起保护作用的判断放在前面。如果把顺序反过来写成 user.startswith("admin") and user is not None,当 user 是 None 时就会抛出 AttributeError。

基于 or 和 and 的写法

因为 or 返回第一个真值操作数,所以可以用一行代码设置默认值:

输出:

Hello, Ada!
Hello, guest!
Hello, guest!

问题在于,or 会替换所有假值,而不只是 None。如果 0、"" 或 [] 是合法的输入,or 会把它丢掉:

当零或空值是有效值时,请使用 x if x is not None else default。if/else 页面介绍了这种条件表达式。

and 也有对应的写法:“只有第一个值存在时才使用这个值”。这种写法不太常见,而且通常写成 if 更清楚,所以要少用:

优先级:先 not,再 and,最后 or

当一个表达式混用这三个运算符时,Python 先运算 not,然后是 and,最后是 or。==、< 和 in 这类比较运算符会在它们之前运算。

同样的值,a or b and c 和 (a or b) and c 给出的结果不同,所以即使默认顺序刚好正确,混合条件也应该加上括号。括号没有任何代价,还能让下一位读代码的人不必琢磨。

and/or 与 &/|

& 和 | 看起来像 and 和 or,有时结果也相同,但它们是不同的运算符。

  • and / or 判断真值、会短路,并返回其中一个操作数。
  • & / | 是按位运算符。用在整数上时,它们按位组合两个数。用在集合上时,表示交集和并集。用在 True 和 False 上时,返回布尔值。它们从不短路:两边总会被求值。

按位运算符的优先级也比比较运算符高,这会让用 & 写的条件变成完全不同的东西:

1 & y 就是 1 & 5,结果是 1,所以中间那行变成了链式比较 5 > 1 > 1,而 1 > 1 为假。在普通 Python 代码中,条件请使用 and、or 和 not。

唯一应该用 &、| 和 ~ 写条件的地方是 NumPy 和 pandas。数组包含许多值,没有单一的真值,所以 and 会抛出 ValueError: The truth value of an array ... is ambiguous。这些库重载了 & 和 |,让它们逐元素运算,所以 pandas 的筛选写作 df[(df.age > 18) & (df.country == "CA")],每个比较都要加括号,原因就是上面说的优先级。

any() 和 all():对整个列表做 and 和 or

要组合一整组条件,请使用内置函数 all()(对每个元素做 and)和 any()(对每个元素做 or)。它们也会以同样的方式短路:

与 and 和 or 不同,any() 和 all() 总是返回布尔值。

常见错误

用 or 把一个变量和多个值比较。下面这个条件永远为真:

Python 把第一个条件读作 (color == "red") or "blue"。比较结果是 False,所以 or 返回 "blue",这是一个非空字符串,也就是真值。请在两边都写出完整的比较,或者更好的做法是用 in 判断成员关系。

写成 && 或 ||。两者都会引发 SyntaxError。请使用 and 和 or。

以为 and/or 会返回布尔值。x = a or b 存的是 a 或 b 本身。这通常正是你想要的,但如果函数应该返回 True 或 False,就用 bool() 转换。

在混合条件中依赖优先级。a or b and c 是合法的,含义也明确,但还是加上括号吧,这样谁都不用去记规则。

常见问题

Python 中 and 运算符有什么作用?

只有两边都为真时,a and b 才为真。Python 先求值 a;如果 a 是假值,就直接返回 a,不再看 b,否则返回 b。用在普通布尔值上时,结果和你预期的一样:True and False 是 False。

为什么 0 or 5 返回的是 5 而不是 True?

or 和 and 返回的是其中一个操作数,而不是一个新的布尔值。如果 x 是真值,x or y 返回 x,否则返回 y。0 是假值,所以 0 or 5 返回 5。如果你需要真正的 True 或 False,就用 bool() 把表达式包起来。

Python 中 and 和 & 有什么区别?

and 是逻辑运算符:它判断真值并且会短路。& 是按位运算符:用在整数上时按位组合(6 & 3 是 2),用在集合上时取交集,而且两边总会被求值。if 条件里请用 and。例外是 NumPy 和 pandas,在那里 & 表示逐元素的“与”。

Python 中 and 和 or 谁先运算?

not 优先级最高,然后是 and,最后是 or。所以 a or b and c 表示 a or (b and c),not a or b 表示 (not a) or b。==、< 这类比较运算符的优先级比这三个都高。

Python 中可以用 && 和 || 吗?

不可以。在 Python 中 && 和 || 会引发 SyntaxError。逻辑运算符是单词 and、or 和 not。单个 & 或 | 是合法的,但含义不同(按位与、按位或)。

Coddy 编程语言插图

和 Coddy 一起学编程

开始