Python & AI Tutorials Logo
Python 编程

9. 使用布尔逻辑组合条件

在第 7 章中,我们学习了布尔值以及使用比较运算符的简单条件。在第 8 章中,我们用这些条件配合 if 语句做出决策。但现实世界的程序往往需要一次检查多个条件。如果用户有正确的密码并且已登录,我们是否应该授予访问权限?如果温度太热或者太冷,我们是否应该显示警告?如果文件为空,我们是否应该继续?

Python 提供了三个逻辑运算符 (logical operators),让我们可以组合并修改布尔值:andornot。这些运算符是你在程序中表达复杂决策逻辑的基础构件。

9.1) 逻辑运算符 and、or 与 not

这三个逻辑运算符会对布尔值(或可以被当作布尔值处理的值)进行运算,并产生新的布尔结果。

9.1.1) and 运算符

and 运算符只有在两个操作数都为真时才返回 True。如果任意一个操作数为假,则整个表达式为假。

python
# 两个条件都必须为 True
age = 25
has_license = True
 
can_rent_car = age >= 21 and has_license
print(can_rent_car)  # Output: True
 
# 如果任意一个条件为 False,结果就是 False
age = 18
can_rent_car = age >= 21 and has_license
print(can_rent_car)  # Output: False

你可以把 and 想成一个严格的门卫:必须所有条件都通过,整体检查才会成功。

and 的真值表:

左操作数右操作数结果
TrueTrueTrue
TrueFalseFalse
FalseTrueFalse
FalseFalseFalse

9.1.2) or 运算符

or 运算符在至少有一个操作数为真时返回 True。只有当两个操作数都为假时,它才返回 False

python
# 至少一个条件必须为 True
is_weekend = True
is_holiday = False
 
can_sleep_in = is_weekend or is_holiday
print(can_sleep_in)  # Output: True
 
# 两个条件都为 False
is_weekend = False
is_holiday = False
can_sleep_in = is_weekend or is_holiday
print(can_sleep_in)  # Output: False

你可以把 or 想成一个宽松的门卫:你只需要满足一个条件就能通过。

or 的真值表:

左操作数右操作数结果
TrueTrueTrue
TrueFalseTrue
FalseTrueTrue
FalseFalseFalse

下面是一个关于折扣资格系统的实用示例:

python
# 如果顾客是学生 OR 老年人,则可享受折扣
age = 68
is_student = False
 
gets_discount = is_student or age >= 65
print(f"Eligible for discount: {gets_discount}")  # Output: Eligible for discount: True
 
# 另一位顾客
age = 30
is_student = False
gets_discount = is_student or age >= 65
print(f"Eligible for discount: {gets_discount}")  # Output: Eligible for discount: False

第一位顾客符合条件,因为他们满足其中一个标准(老年人),即使他们不是学生。

9.1.3) not 运算符

not 运算符是一个一元运算符 (unary operator)(它只作用于单个操作数),用于反转布尔值。它会把 True 变成 False,把 False 变成 True

python
is_raining = False
is_sunny = not is_raining
print(is_sunny)  # Output: True
 
is_raining = True
is_sunny = not is_raining
print(is_sunny)  # Output: False

not 的真值表:

操作数结果
TrueFalse
FalseTrue

当你想检查某个条件的相反情况时,not 运算符特别有用:

python
# 检查文件是否 NOT 为空
file_size = 0
is_empty = file_size == 0
is_not_empty = not is_empty
print(f"File has content: {is_not_empty}")  # Output: File has content: False
 
# 检查用户是否 NOT 已登录
is_logged_in = False
needs_login_prompt = not is_logged_in
print(f"Show login prompt: {needs_login_prompt}")  # Output: Show login prompt: True

9.1.4) 组合多个逻辑运算符

你可以在一个表达式中组合多个逻辑运算符,以构建更复杂的条件:

python
# 在线商店:如果订单超过 $50 或者客户是高级会员,则免运费
# 并且商品有库存
order_total = 45.00
is_premium = True
in_stock = True
 
gets_free_shipping = (order_total >= 50 or is_premium) and in_stock
print(f"Free shipping: {gets_free_shipping}")  # Output: Free shipping: True

让我们跟踪一下它的求值过程:

  1. order_total >= 50 的结果是 False(45.00 不 >= 50)
  2. is_premiumTrue
  3. False or True 的结果是 True
  4. in_stockTrue
  5. True and True 的结果是 True

下面是另一个访问控制的示例:

python
# 如果用户是管理员
# 并且(在内网 OR 使用 VPN),则可访问管理面板
is_admin = True
on_internal_network = False
using_vpn = True
 
can_access_admin = is_admin and (on_internal_network or using_vpn)
print(f"Can access admin panel: {can_access_admin}")  # Output: Can access admin panel: True

注意 (on_internal_network or using_vpn) 外面的括号。这些括号很重要,因为它们会控制求值顺序,就像算术表达式中的括号一样。

9.2) 布尔表达式中的运算符优先级(Not、And、Or 的顺序)

当你在不加括号的情况下组合多个逻辑运算符时,Python 会遵循特定的优先级规则来决定求值顺序。理解这些规则能帮助你写出正确的条件,并避免一些隐蔽的 bug。

9.2.1) 优先级层级

Python 按以下顺序(从高到低优先级)对逻辑运算符求值:

  1. not(最高优先级)
  2. and(中等优先级)
  3. or(最低优先级)

这意味着会先计算 not,然后是 and,最后是 or

python
# 不加括号时,优先级决定求值顺序
result = True or False and False
print(result)  # Output: True
 
# Python 如何求值:
# 第 1 步:False and False → False(and 的优先级高于 or)
# 第 2 步:True or False → True

我们用一个更详细的示例一步步看看:

python
is_weekend = False
is_holiday = True
has_work = True
 
# 表达式:not has_work or is_weekend and is_holiday
free_time = not has_work or is_weekend and is_holiday
 
# 求值顺序:
# 第 1 步:not has_work → not True → False
# 第 2 步:is_weekend and is_holiday → False and True → False
# 第 3 步:False or False → False
print(f"Has free time: {free_time}")  # Output: Has free time: False

9.2.2) 使用括号提升清晰度

即使你理解优先级规则,使用括号也能让代码更清晰,并防止出错。括号会覆盖默认优先级,让你的意图更明确。

python
# 不加括号时含义不够明确
result = True or False and False
print(result)  # Output: True
 
# 加括号后更清晰——我们真正想表达什么?
result = (True or False) and False
print(result)  # Output: False
 
result = True or (False and False)
print(result)  # Output: True

这两个表达式会产生不同的结果!括号会彻底改变含义。

9.2.3) 比较运算符与逻辑运算符一起使用

比较运算符(如 <>==!=)的优先级高于逻辑运算符。这意味着比较会在逻辑运算之前被求值。

python
age = 25
income = 50000
 
# 比较表达式周围不需要括号
eligible = age >= 18 and income >= 30000
print(f"Eligible for loan: {eligible}")  # Output: Eligible for loan: True
 
# Python 的求值方式:
# 第 1 步:age >= 18 → True
# 第 2 步:income >= 30000 → True
# 第 3 步:True and True → True

9.3) 短路求值 (short-circuit evaluation)

Python 在使用 andor 计算布尔表达式时,会使用短路求值 (short-circuit evaluation)。这意味着 Python 一旦知道最终结果,就会停止继续求值,从而可能跳过后续操作数的计算。这种行为既是一种性能优化,也是一种实用的编程技巧。

9.3.1) and 如何短路

对于 and 运算符,如果左操作数是 False,Python 就知道整个表达式必然是 False(因为 and 要返回 True,两个操作数都必须为真)。因此,Python 完全不会计算右操作数。

python
# 简单演示
x = 5
result = x < 3 and x > 10
print(result)  # Output: False
 
# Python 的求值过程:
# 第 1 步:x < 3 → 5 < 3 → False
# 第 2 步:由于左侧为 False,不计算 x > 10
# 第 3 步:返回 False

下面是一个实际示例,展示短路求值为什么重要:

python
# 检查一个数是否可整除——避免除以零
numerator = 100
denominator = 0
 
# 由于短路求值,这样写是安全的
# 如果 denominator 为 0,就不会发生除法
is_divisible = denominator != 0 and numerator % denominator == 0
print(f"Is divisible: {is_divisible}")  # Output: Is divisible: False
 
# 如果没有短路求值,这将导致错误:
# denominator = 0
# result = numerator % denominator  # ZeroDivisionError!

表达式 denominator != 0 的结果为 False,因此 Python 永远不会去计算 numerator % denominator,否则会触发除以零错误。

我们再看一个关于字符串操作的示例:

python
# 安全地检查字符串属性
text = ""
 
# 检查 text 不为空 AND 第一个字符是大写
# 安全,因为如果 text 为空,我们不会尝试访问 text[0]
has_uppercase_start = len(text) > 0 and text[0].isupper()
print(f"Starts with uppercase: {has_uppercase_start}")  # Output: Starts with uppercase: False
 
# 如果不做长度检查就这样写:
# text = ""
# result = text[0].isupper()  # IndexError: string index out of range

9.3.2) or 如何短路

对于 or 运算符,如果左操作数是 True,Python 就知道整个表达式必然是 True(因为只要至少一个操作数为真就足够了)。因此,Python 不会计算右操作数。

python
# 简单演示
x = 15
result = x > 10 or x < 5
print(result)  # Output: True
 
# Python 的求值过程:
# 第 1 步:x > 10 → 15 > 10 → True
# 第 2 步:由于左侧为 True,不计算 x < 5
# 第 3 步:返回 True

9.3.3) 短路求值的实际应用

避免错误:

python
# 安全地访问列表元素
numbers = [1, 2, 3]
index = 5
 
# 访问前先检查 index 是否有效
is_valid = index < len(numbers) and numbers[index] > 0
print(f"Valid and positive: {is_valid}")  # Output: Valid and positive: False
 
# 如果没有短路求值,这会崩溃:
# is_valid = numbers[index] > 0  # IndexError!

高效地检查多个条件:

python
# 表单校验——在第一个错误处停止
email = "user@example.com"
password = "pass"
age = 25
 
# 按照最可能失败的顺序依次检查每个要求
valid_form = (
    len(email) > 0 and              # 快速检查
    "@" in email and                # 快速检查
    len(password) >= 8 and          # 快速检查
    age >= 18                       # 快速检查
)
print(f"Form valid: {valid_form}")  # Output: Form valid: False
# 在密码长度检查处停止,不会计算 age

and 表达式

左侧为 True?

返回 False
跳过右侧

计算右侧

返回右侧结果

or 表达式

左侧为 True?

返回 True
跳过右侧

计算右侧

返回右侧结果

9.4) 当操作数不是布尔值时 and 与 or 会返回什么,以及常见布尔表达式陷阱

到目前为止,我们看到 andornot 都在布尔值上工作。但 Python 的逻辑运算符有一个有趣的行为:它们可以对任何值进行运算,而不仅仅是 TrueFalse。理解这种行为能帮助你写出更简洁的代码,并避免常见错误。

9.4.1) 理解真值性与假值性(回顾)

正如我们在第 7 章所学,Python 会在布尔上下文中把许多非布尔值当作“真值 (truthy)”或“假值 (falsy)”:

假值(会被当作 False):

  • False
  • None
  • 0(任何数值类型的零)
  • ""(空字符串)
  • [](空列表)
  • {}(空字典)
  • ()(空元组)

真值(会被当作 True):

  • True
  • 任何非零数字
  • 任何非空字符串
  • 任何非空集合
python
# 演示真值性
if "hello":
    print("Non-empty strings are truthy")  # Output: Non-empty strings are truthy
 
if 0:
    print("This won't print")  # 0 是假值
else:
    print("Zero is falsy")  # Output: Zero is falsy
 
if [1, 2, 3]:
    print("Non-empty lists are truthy")  # Output: Non-empty lists are truthy

9.4.2) and 实际返回什么

and 运算符并不总是返回 TrueFalse。相反,它会返回其中一个操作数

  • 如果左操作数为假值,and 返回左操作数(不会计算右侧)
  • 如果左操作数为真值,and 返回右操作数
python
# and 会返回第一个假值;如果全部都是真值,则返回最后一个值
result = 5 and 10
print(result)  # Output: 10
 
result = 0 and 10
print(result)  # Output: 0
 
result = "hello" and "world"
print(result)  # Output: world
 
result = "" and "world"
print(result)  # Output: (empty string)
 
result = None and "world"
print(result)  # Output: None

让我们跟踪这些示例:

python
# 示例 1:两边都是真值
result = 5 and 10
# 第 1 步:5 是真值,所以计算右侧
# 第 2 步:返回右侧的值:10
print(result)  # Output: 10
 
# 示例 2:左侧是假值
result = 0 and 10
# 第 1 步:0 是假值,所以立刻返回它
# 第 2 步:不计算右侧
print(result)  # Output: 0
 
# 示例 3:两个都是真值字符串
result = "hello" and "world"
# 第 1 步:"hello" 是真值,所以计算右侧
# 第 2 步:返回右侧的值:"world"
print(result)  # Output: world

9.4.3) or 实际返回什么

类似地,or 运算符也会返回其中一个操作数:

  • 如果左操作数为真值,or 返回左操作数(不会计算右侧)
  • 如果左操作数为假值,or 返回右操作数
python
# or 会返回第一个真值;如果全部都是假值,则返回最后一个值
result = 5 or 10
print(result)  # Output: 5
 
result = 0 or 10
print(result)  # Output: 10
 
result = "" or "default"
print(result)  # Output: default
 
result = "hello" or "world"
print(result)  # Output: hello
 
result = None or 0
print(result)  # Output: 0

让我们跟踪这些示例:

python
# 示例 1:左侧是真值
result = 5 or 10
# 第 1 步:5 是真值,所以立刻返回它
# 第 2 步:不计算右侧
print(result)  # Output: 5
 
# 示例 2:左侧是假值
result = 0 or 10
# 第 1 步:0 是假值,所以计算右侧
# 第 2 步:返回右侧的值:10
print(result)  # Output: 10
 
# 示例 3:两边都是假值
result = None or 0
# 第 1 步:None 是假值,所以计算右侧
# 第 2 步:返回右侧的值:0(尽管它同样是假值)
print(result)  # Output: 0

9.4.4) 使用 or 提供默认值的实际用途

一个常见模式是用 or 来提供默认值:

python
# 带默认值的用户偏好设置
user_theme = ""  # 用户没有设置主题
theme = user_theme or "light"
print(f"Theme: {theme}")  # Output: Theme: light
 
user_theme = "dark"
theme = user_theme or "light"
print(f"Theme: {theme}")  # Output: Theme: dark
 
# 配置值
max_retries = None  # 未配置
retries = max_retries or 3
print(f"Retries: {retries}")  # Output: Retries: 3
 
max_retries = 5
retries = max_retries or 3
print(f"Retries: {retries}")  # Output: Retries: 5

这个模式之所以可行,是因为如果左侧是假值(空字符串、None0 等),or 就会返回右侧(默认值)。

and

or

使用非布尔值的逻辑运算符

运算符类型

左侧是假值?

左侧是真值?

返回左侧值

返回右侧值

返回左侧值

返回右侧值

9.4.12) 运算符返回值总结

下面是每个逻辑运算符返回内容的完整总结:

and 运算符:

  • 返回第一个假值操作数
  • 如果所有操作数都是真值,则返回最后一个操作数
  • 使用短路求值(在第一个假值处停止)

or 运算符:

  • 返回第一个真值操作数
  • 如果所有操作数都是假值,则返回最后一个操作数
  • 使用短路求值(在第一个真值处停止)

not 运算符:

  • 总是返回布尔值(TrueFalse
  • not 会先把操作数转换为布尔值,然后再取反
python
# 演示三个运算符
print(5 and 10)           # Output: 10 (both truthy, return last)
print(0 and 10)           # Output: 0 (first falsy, return it)
print(5 or 10)            # Output: 5 (first truthy, return it)
print(0 or 10)            # Output: 10 (first falsy, evaluate second)
print(not 5)              # Output: False (5 is truthy, not returns boolean)
print(not 0)              # Output: True (0 is falsy, not returns boolean)
print(not "")             # Output: True (empty string is falsy)
print(not "hello")        # Output: False (non-empty string is truthy)

理解这些行为能帮助你写出更简洁、更 Pythonic 的代码,但始终要优先考虑清晰性。如果使用这些特性让你的代码更难理解,那么显式写法会更好。


在本章中,我们探讨了如何使用 Python 的 andornot 运算符把简单条件组合成复杂的布尔逻辑。我们学习了运算符优先级、短路求值,以及逻辑运算符在非布尔值上的“意外”行为。我们还考察了常见陷阱与编写清晰、正确布尔表达式的最佳实践。

这些工具让你能够在程序中表达更复杂的决策逻辑。结合第 8 章的 if 语句,你现在几乎可以处理程序所需的任何条件逻辑。在下一章中,我们将探索条件表达式,它提供了一种更紧凑的方式,可基于条件在两个值之间进行选择。

© 2025. Primesoft Co., Ltd.
support@primesoft.ai