7. 布尔值与条件
在前面的章节中,你已经学习了如何处理数字、字符串以及基础的数据操作。现在我们要开始探索 Python 如何做出决策——这是一种关键能力,它让程序可以根据不同的条件做出不同的响应。在 Python 的决策机制核心,就是布尔值(Boolean 值)和条件(conditions)。
想想日常生活中的决策:“如果在下雨,我就带伞。”“如果温度高于 30°C,我就打开空调。”这些决策都基于某些条件,而这些条件要么为真,要么为假。Python 使用同样的原则:它会对条件求值,以确定它们是真还是假,然后根据结果采取相应的动作。
在本章中,我们将探索布尔值,学习如何使用比较运算符创建条件,理解 Python 的“真值性(truthiness)”概念,并发现构建复杂逻辑表达式的强大技巧。读完本章后,你就掌握了在后续章节中编写 if 语句和循环所需要的基础构件。
7.1) 布尔值 True 和 False
Python 有一种特殊的数据类型叫 bool(Boolean 的缩写),用来表示真值。这个类型只有两个可能的取值:True 和 False。注意这两个值首字母是大写的——在 Python 中这一点非常重要。写成小写的 true 或 false 会导致错误,因为 Python 不会把它们识别为布尔值。
# boolean_basics.py
# 创建布尔变量
is_sunny = True
is_raining = False
print(is_sunny) # Output: True
print(is_raining) # Output: False
print(type(True)) # Output: <class 'bool'>
print(type(False)) # Output: <class 'bool'>布尔值非常基础,因为它们可以表示程序中任何“是/否”问题的结果:这个数字是否大于 10?这个字符串是否包含字母 'a'?用户是否已经登录?Python 中的每个条件最终都会求值为 True 或 False。
7.1.1) 变量和表达式中的布尔值
你可以像存储数字或字符串那样,把布尔值存储在变量中。当你想在程序中跟踪某种状态时,这非常有用:
# boolean_variables.py
# 使用布尔变量跟踪状态
is_logged_in = False
has_permission = True
is_valid_email = True
print("User logged in:", is_logged_in) # Output: User logged in: False
print("Has permission:", has_permission) # Output: Has permission: True
print("Valid email:", is_valid_email) # Output: Valid email: True
# 布尔变量是可以重新赋值的
is_logged_in = True
print("User logged in:", is_logged_in) # Output: User logged in: True布尔变量通常会使用 is_、has_、can_ 这样的前缀命名,以便清晰表达用途。这种命名约定可以帮助你和阅读你代码的人一眼看出,这个变量存储的是一个真/假值。
7.2) 比较运算符与基础条件
虽然你可以直接使用 True 和 False 创建布尔值,但在程序中,大多数布尔值其实来自于比较(comparisons)——也就是测试两个值之间的关系。Python 提供了若干比较运算符(comparison operators),用于比较两个值,并生成一个布尔结果。
7.2.1) 六种比较运算符
Python 有六个主要的比较运算符:
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
== | 等于 | 5 == 5 | True |
!= | 不等于 | 5 != 3 | True |
< | 小于 | 3 < 5 | True |
> | 大于 | 5 > 3 | True |
<= | 小于等于 | 5 <= 5 | True |
>= | 大于等于 | 5 >= 3 | True |
来看一下这些运算符的实际效果:
# comparison_operators.py
# 比较数字
x = 10
y = 20
print(x == y) # Output: False
print(x != y) # Output: True
print(x < y) # Output: True
print(x > y) # Output: False
print(x <= y) # Output: True
print(x >= y) # Output: False
# 比较相等的值
a = 15
b = 15
print(a == b) # Output: True
print(a <= b) # Output: True
print(a >= b) # Output: True7.2.2) 比较数字和字符串
比较运算符不仅适用于整数,还能用于许多其他类型的值:
# comparing_types.py
# 比较浮点数
temperature = 23.5
print(temperature > 20.0) # Output: True
print(temperature == 23.5) # Output: True
# 比较字符串(按字母/字典序)
name1 = "Alice"
name2 = "Bob"
print(name1 < name2) # Output: True
print(name1 == name2) # Output: False
# 字符串比较是区分大小写的
word1 = "Python"
word2 = "python"
print(word1 == word2) # Output: False
# 使用 len() 比较字符串长度
print(len(name1) == len(name2)) # Output: False在比较字符串时,Python 使用的是字典序(lexicographic ordering),本质上是基于 Unicode 字符值的“字母顺序”。在这种排序中,大写字母排在小写字母之前,这也是为什么 "Python" 和 "python" 不相等的原因。
7.2.3) 存储比较结果
任何比较的结果都是一个布尔值,所以你可以把它存储在变量中:
# storing_comparisons.py
# 将比较结果存储以便后续使用
age = 25
is_adult = age >= 18
is_senior = age >= 65
is_teenager = 13 <= age <= 19 # 我们很快会学习链式比较
print("Is adult:", is_adult) # Output: Is adult: True
print("Is senior:", is_senior) # Output: Is senior: False
print("Is teenager:", is_teenager) # Output: Is teenager: False
# 在计算或其他表达式中使用已存储的比较结果
price = 100
discount_eligible = price > 50
print("Discount eligible:", discount_eligible) # Output: Discount eligible: True将比较结果存储在描述性命名的变量中可以让代码更易读。与其在程序中反复写 age >= 18,不如使用变量 is_adult,它更清楚地表达了你要检查的含义。
7.3) 来自比较、表达式和 bool() 函数的布尔值
我们已经看到,比较会产生布尔值。但在 Python 中还有其他方式可以得到布尔值,其中包括使用 bool() 函数把其他类型转换为布尔类型。
7.3.1) bool() 函数
bool() 函数会把任意值转换为对应的布尔值。当你想显式检查某个值在布尔上下文中会被视为真还是假时,这非常有用:
# bool_function.py
# 将数字转换为布尔值
print(bool(1)) # Output: True
print(bool(42)) # Output: True
print(bool(-5)) # Output: True
print(bool(0)) # Output: False
print(bool(0.0)) # Output: False
# 将字符串转换为布尔值
print(bool("Hello")) # Output: True
print(bool("False")) # Output: True
print(bool("")) # Output: False
# 将 None 转换为布尔值
print(bool(None)) # Output: Falsebool() 函数遵循特定的转换规则,我们会在下一节关于真值性(truthiness)和假值性(falsiness)中详细探讨。现在你可以先注意一点:大多数值都会转换为 True,但一些特殊值,比如 0、0.0、空字符串("")和 None 会转换为 False。
7.4) 条件中的真值性与假值性
Python 的一个强大特性是它的真值性(truthiness)与假值性(falsiness)概念。在 Python 中,不仅 True 和 False 有布尔意义,每一个值在布尔上下文中都有其固有的布尔解释。这意味着在任何需要布尔值的地方,你都可以直接使用其他类型的值,Python 会把它们当作真或假来处理。
7.4.1) 假值:哪些值被视为 False
在 Python 中,下面这些值被认为是假值(falsy)(在布尔上下文中表现得像 False):
- 布尔值本身的
False - 特殊值
None - 各种形式的数字零:
0、0.0、0j(复数零) - 空序列:
""(空字符串)、[](空列表)、()(空元组) - 空映射:
{}(空字典) - 空集合:
set()
我们用 bool() 函数来验证一下:
# falsy_values.py
# 下面这些值都是假值
print("Boolean False:", bool(False)) # Output: Boolean False: False
print("None:", bool(None)) # Output: None: False
print("Zero integer:", bool(0)) # Output: Zero integer: False
print("Zero float:", bool(0.0)) # Output: Zero float: False
print("Empty string:", bool("")) # Output: Empty string: False
print("Empty list:", bool([])) # Output: Empty list: False
print("Empty tuple:", bool(())) # Output: Empty tuple: False
print("Empty dict:", bool({})) # Output: Empty dict: False7.4.2) 真值:其他的一切
除了上述假值之外,Python 中的其他所有值都被视为真值(truthy)(在布尔上下文中表现得像 True)。包括:
- 布尔值本身的
True - 任意非零数字(正数或负数)
- 任意非空字符串、列表、元组、字典或集合
- 大多数你自己创建的对象
# truthy_values.py
# 下面这些值都是真值
print("Boolean True:", bool(True)) # Output: Boolean True: True
print("Positive integer:", bool(42)) # Output: Positive integer: True
print("Negative integer:", bool(-1)) # Output: Negative integer: True
print("Non-zero float:", bool(3.14)) # Output: Non-zero float: True
print("Non-empty string:", bool("Hello")) # Output: Non-empty string: True
print("String 'False':", bool("False")) # Output: String 'False': True
print("String '0':", bool("0")) # Output: String '0': True
print("Non-empty list:", bool([1, 2, 3])) # Output: Non-empty list: True
print("Non-empty tuple:", bool((1,))) # Output: Non-empty tuple: True
print("Non-empty dict:", bool({"a": 1})) # Output: Non-empty dict: True重要说明:字符串 "False" 是真值,因为它是一个非空字符串。字符串 "0" 也是真值,原因相同。只有真正的布尔值 False 和数字值 0 是假值。
7.4.3) 为什么真值性很重要
理解真值性非常重要,因为它是 Python 中的一个基础概念,你会频繁遇到。在第 8 章,你将学习根据条件做出决策的 if 语句。这些语句可以使用任意值,而不仅仅是显式的布尔比较,因为 Python 会自动根据真值性来评估这些值。
真值性让你可以写出简洁的代码,用来检查集合是否包含元素、字符串是否有内容、可选值是否存在。下面是一个关于真值性的预览示例(我们会在第 8 章学习完整的 if 语句语法):
# truthiness_preview.py
# 使用 bool() 演示真值性
# (在第 8 章中,我们会直接在 if 语句中使用这些)
# 检查字符串是否有内容
user_input = ""
has_content = bool(user_input)
print("User entered something:", has_content) # Output: User entered something: False
user_input = "Alice"
has_content = bool(user_input)
print("User entered something:", has_content) # Output: User entered something: True
# 检查列表是否有元素
shopping_list = []
has_items = bool(shopping_list)
print("Shopping list has items:", has_items) # Output: Shopping list has items: False
shopping_list = ["milk", "eggs", "bread"]
has_items = bool(shopping_list)
print("Shopping list has items:", has_items) # Output: Shopping list has items: True
# 检查值是否存在(不是 None)
optional_value = None
value_exists = bool(optional_value)
print("Value exists:", value_exists) # Output: Value exists: False理解真值性可以让你的代码更“Pythonic”——也就是更符合 Python 的习惯和风格。当你看到有经验的 Python 程序员在检查条件时没有写出显式比较,他们往往就是在利用真值性来编写更简洁、可读性更高的代码。
7.5) 链式比较与常见布尔陷阱
Python 提供了一个强大的特性,叫作链式比较(chained comparisons),它能让某些条件写起来更简洁,也更接近数学记号。不过,这个特性以及布尔逻辑的其他方面,也可能导致一些常见错误。
7.5.1) 链式比较
在数学中,你可能会写 “10 < x < 20” 来表示 x 介于 10 和 20 之间。Python 允许你以完全相同的方式来写:
# chained_comparisons.py
# 检查一个值是否在某个范围内
x = 15
# 数学写法(链式比较)
in_range = 10 < x < 20
print("x is between 10 and 20:", in_range) # Output: x is between 10 and 20: True
# 它等价于组合两个比较
# (我们会在第 9 章学习 'and')
in_range = (10 < x) and (x < 20)
print("x is between 10 and 20:", in_range) # Output: x is between 10 and 20: True
# 使用范围之外的值进行测试
y = 25
in_range = 10 < y < 20
print("y is between 10 and 20:", in_range) # Output: y is between 10 and 20: False
# 使用边界值进行测试
z = 10
in_range = 10 < z < 20
print("z is between 10 and 20:", in_range) # Output: z is between 10 and 20: False
# 使用 <= 和 >= 包含边界
in_range_inclusive = 10 <= z <= 20
print("z is between 10 and 20 (inclusive):", in_range_inclusive) # Output: z is between 10 and 20 (inclusive): True链式比较的工作方式是:依次评估每一对相邻项。表达式 10 < x < 20 实际上会被解释为 (10 < x) and (x < 20)。只有当链中所有比较都为真时,整个链式比较才为真。
你也可以链上两个以上的比较:
# multiple_chains.py
# 链式比较多个条件
a = 5
b = 10
c = 15
d = 20
# 检查这些值是否按升序排列
ascending = a < b < c < d
print("Values are in ascending order:", ascending) # Output: Values are in ascending order: True下面是链式比较工作方式的可视化表示:
7.5.2) 常见陷阱:赋值 vs 比较
初学者最常见的错误之一,是在需要比较(==)的地方写成了赋值(=):
# assignment_vs_comparison_pitfall.py
x = 10
# 这是赋值,不是比较
# 它是把 20 赋值给 x,而不是比较 x 和 20
# x = 20 # 这会把 x 改成 20
# 这是比较
result = (x == 20) # 这是在检查 x 是否等于 20
print("x equals 20:", result) # Output: x equals 20: False
print("x is now:", x) # Output: x is now: 10
# 在第 8 章中,你会看到,在条件中使用 = 会导致错误
# if x = 20: # SyntaxError: invalid syntax
# print("This won't work")
# 正确的比较方式
# 在第 8 章中,你会写:if x == 10:
result = x == 10
print("x equals 10:", result) # Output: x equals 10: True在 if 语句中(你会在第 8 章学习),Python 会通过抛出 SyntaxError 来帮助你避免这个错误:如果你在条件里使用赋值号,就会报错。但是在其他上下文中,如果你本来想用 == 却写成了 =,就可能产生一些很隐蔽、难以发现的 bug。
7.5.3) 常见陷阱:浮点数比较
在比较浮点数时,你需要注意精度问题:
# floating_point_comparison.py
# 浮点数运算可能存在精度问题
result = 0.1 + 0.2
print("0.1 + 0.2 =", result) # Output: 0.1 + 0.2 = 0.30000000000000004
# 直接比较可能得不到预期结果
is_equal = (result == 0.3)
print("Result equals 0.3:", is_equal) # Output: Result equals 0.3: False
# 实际值非常接近 0.3,但并不完全等于 0.3
print("Difference:", result - 0.3) # Output: Difference: 5.551115123125783e-17
# 对浮点数进行比较时,应使用一个小的容差
tolerance = 0.0001
is_close = abs(result - 0.3) < tolerance
print("Result is close to 0.3:", is_close) # Output: Result is close to 0.3: True
# Python 3.5+ 提供了 math.isclose() 函数
# 我们会在第 23 章学习如何导入模块
# import math
# is_close = math.isclose(result, 0.3)出现这种问题的原因在于:计算机以二进制形式存储浮点数,而某些十进制数(比如 0.1)无法被二进制精确表示。当你对这些数进行运算时,很小的舍入误差就会累积。对于大多数实际应用,这些误差可以忽略不计,但它们可能导致直接使用相等比较时得到出人意料的结果。
7.6) 布尔值作为整数(1 和 0)以及为何应避免算术运算
有一个令人惊讶的事实:在 Python 中,True 和 False 实际上是整数的一种特殊情况。True 等价于 1,False 等价于 0。这是 Python 设计中的一个历史遗留,但它带来了一些有趣的含义。理解这种关系可以帮助你避免困惑,但在实际代码中你几乎不需要直接利用这一点。
7.6.1) 布尔值就是整数
你可以通过检查类型并进行整数运算,来验证布尔值就是整数:
# booleans_as_integers.py
# 布尔类型是整数类型的子类型
print(isinstance(True, int)) # Output: True
print(isinstance(False, int)) # Output: True
# True 等于 1,False 等于 0
print(True == 1) # Output: True
print(False == 0) # Output: True
print(True == 2) # Output: False
# 你可以在算术运算中使用布尔值(但不推荐这样做!)
result = True + True
print("True + True =", result) # Output: True + True = 2
result = True + False
print("True + False =", result) # Output: True + False = 1
result = False * 100
print("False * 100 =", result) # Output: False * 100 = 0
# 布尔值可以用作列表索引
items = ["first", "second", "third"]
print(items[False]) # Output: first
print(items[True]) # Output: secondisinstance() 函数用于检查某个值是否是某个类型的实例。当我们检查 isinstance(True, int) 时,Python 返回 True,因为 bool 类型是 int 类型的子类。
7.6.2) 为什么应避免布尔算术运算
尽管 Python 允许你在算术运算中使用布尔值,但你应尽量避免这么做。把布尔值当作数字来使用会让代码变得令人困惑、难以理解。布尔值和整数之间的这种关系,主要是一个你需要了解但不常直接利用的历史细节。
下面的例子说明了为什么布尔算术是有问题的:
# boolean_arithmetic_problems.py
# 令人困惑的代码示例
count = 0
has_error = True
has_warning = False
# 这段代码虽然能运行,但非常令人困惑
total = count + has_error + has_warning
print("Total:", total) # Output: Total: 1
# 这到底是什么意思?并不直观!
# 更好的做法是:显式表达意图
# 我们会在第 10 章学习 if-else 表达式。
error_count = 1 if has_error else 0
warning_count = 1 if has_warning else 0
total = count + error_count + warning_count
print("Total:", total) # Output: Total: 1唯一比较常见、可以接受的例外情况是,在统计容器中有多少个 True 值时:
# counting_trues.py
# 统计有多少个条件为真
conditions = [True, False, True, True, False]
# 这种用法是可以接受的,因为意图很清晰
true_count = sum(conditions)
print("Number of true conditions:", true_count) # Output: Number of true conditions: 3
# 这是因为 sum() 会把值累加起来
# True 被当作 1,False 被当作 0
# 所以 sum([True, False, True, True, False]) = 1 + 0 + 1 + 1 + 0 = 3在这种场景下,对布尔列表使用 sum() 是一种在 Python 中广泛使用的惯用法。其意图——统计有多少个条件为真——从上下文中就可以清楚看出。
7.6.3) 布尔值与类型转换
因为布尔值本身就是整数,所以把它们显式转换为整数是多余的:
# boolean_conversion.py
# 将布尔值转换为整数(没有必要)
value = True
int_value = int(value)
print("Integer value:", int_value) # Output: Integer value: 1
print("Are they equal?", value == int_value) # Output: Are they equal? True
# 但将整数转换为布尔值是有用的
number = 0
bool_value = bool(number)
print("Boolean value:", bool_value) # Output: Boolean value: False
number = 42
bool_value = bool(number)
print("Boolean value:", bool_value) # Output: Boolean value: True使用 int() 将布尔值转换为整数是没有必要的,因为布尔值本身已经以整数的方式工作了。然而,使用 bool() 将整数(或其他类型)转换为布尔值在你想显式检查真值性时很有用。
7.6.4) 历史原因
布尔值在 Python 中是整数的原因,源于历史。早期的 Python 版本(2.3 之前)并没有单独的布尔类型。程序员使用 1 表示真,0 表示假。当后来引入 bool 类型时,为了与已有代码保持向后兼容,就把它设计成 int 的子类。
在今天,你应该首先把 True 和 False 当作布尔值来思考。它们与 1 和 0 的关系主要是一个实现细节,只有在某些意想不到的地方遇到它时,你才需要考虑它。
7.7) 在条件中使用 in 和 not in
运算符 in 和 not in 用于测试成员关系(membership)——也就是某个值是否存在于某个集合中。在处理字符串、列表以及其他容器时,这些运算符对于写出可读性很强的条件非常有用。
7.7.1) 检查字符串中的成员关系
运算符 in 可以检查一个字符串是否出现在另一个字符串中:
# string_membership.py
# 检查子字符串是否存在于字符串中
text = "Python programming is fun"
# 使用 'in' 检查子字符串
has_python = "Python" in text
print("Contains 'Python':", has_python) # Output: Contains 'Python': True
has_java = "Java" in text
print("Contains 'Java':", has_java) # Output: Contains 'Java': False
# 区分大小写的匹配
has_python_lower = "python" in text
print("Contains 'python':", has_python_lower) # Output: Contains 'python': False
# 使用 'not in' 检查不存在
has_no_java = "Java" not in text
print("Does not contain 'Java':", has_no_java) # Output: Does not contain 'Java': Truein 运算符执行的是区分大小写的搜索。如果你需要大小写不敏感的搜索,可以先把两个字符串都转换为同一种大小写:
# case_insensitive_search.py
text = "Python Programming"
# 区分大小写的搜索(无法匹配)
result = "python" in text
print("Contains 'python':", result) # Output: Contains 'python': False
# 大小写不敏感搜索(将两者都转换为小写)
result = "python" in text.lower()
print("Contains 'python' (case-insensitive):", result) # Output: Contains 'python' (case-insensitive): True
# 原始字符串保持不变
print("Original text:", text) # Output: Original text: Python Programming7.7.2) 检查列表中的成员关系
运算符 in 也可以作用于列表(我们会在第 14 章详细学习列表):
# list_membership.py
# 检查值是否存在于列表中
numbers = [1, 2, 3, 4, 5]
has_three = 3 in numbers
print("List contains 3:", has_three) # Output: List contains 3: True
has_ten = 10 in numbers
print("List contains 10:", has_ten) # Output: List contains 10: False
# 使用 'not in'
missing_ten = 10 not in numbers
print("List does not contain 10:", missing_ten) # Output: List does not contain 10: True
# 在字符串列表中同样适用
fruits = ["apple", "banana", "cherry"]
has_banana = "banana" in fruits
print("List contains 'banana':", has_banana) # Output: List contains 'banana': True
has_grape = "grape" in fruits
print("List contains 'grape':", has_grape) # Output: List contains 'grape': False7.7.3) 使用 range 进行成员测试
你也可以对 range 对象使用 in(我们会在第 12 章学习 range):
# range_membership.py
# 检查数字是否在某个 range 中
age = 25
# 将 'in' 用于 range
is_adult = age in range(18, 100)
print("Is adult:", is_adult) # Output: Is adult: True
# 不过,对于数值范围比较,用比较运算符通常更高效
is_adult = 18 <= age < 100
print("Is adult:", is_adult) # Output: Is adult: True
# 当你需要特定的值序列时,'in' 和 range 很有用
valid_ages = range(18, 66) # 工作年龄段
is_working_age = age in valid_ages
print("Is working age:", is_working_age) # Output: Is working age: True7.7.4) not in 运算符
运算符 not in 与 in 相反——当某个值不在集合中时返回 True:
# not_in_operator.py
# 使用 'not in' 来获得更清晰的逻辑
allowed_users = ["alice", "bob", "charlie"]
current_user = "eve"
# 使用 'not in' 更加易读
is_unauthorized = current_user not in allowed_users
print("User is unauthorized:", is_unauthorized) # Output: User is unauthorized: True
# 检查必填字段是否缺失
provided_fields = ["name", "email"]
# 查找缺失字段
missing_name = "name" not in provided_fields
missing_email = "email" not in provided_fields
missing_password = "password" not in provided_fields
print("Missing name:", missing_name) # Output: Missing name: False
print("Missing email:", missing_email) # Output: Missing email: False
print("Missing password:", missing_password) # Output: Missing password: Truein 和 not in 运算符可以让你的条件表达式更加可读和富有表现力。与其写复杂的比较,不如直接表达“某个值是否在某个集合中?”,这更贴近你对问题的自然思考方式。
在本章中,你学习了 Python 中布尔值和条件的基础知识。你现在已经理解了:
- 布尔值
True和False以及它们在决策中的作用 - 用于创建条件的比较运算符(
==、!=、<、>、<=、>=) bool()函数及其将值转换为布尔值的用法- 真值性和假值性——Python 如何把所有值视为真或假
- 用于可读范围检查的链式比较
- 在处理布尔值时需要避免的常见陷阱
- 布尔值与整数之间的关系(以及为什么要避免布尔算术)
- 使用
in和not in运算符进行成员测试
这些概念构成了你将在接下来章节中学习的控制流结构的基础。在第 8 章中,你会在 if 语句中使用布尔表达式,让程序根据不同条件作出响应。在第 9 章中,你将学习如何使用 and、or 和 not 等逻辑运算符组合多个条件。在第 10 和第 11 章中,你会使用条件来控制循环,使某些操作得以重复执行。
理解布尔值和条件对于编写能够做出决策、验证输入、并能根据不同情形作出适当响应的程序至关重要。多练习这些概念,你会发现,在 Python 中进行决策会变得自然而直观。