Python: 运算符全览
编程本质上就是"拿数据做运算"。第02课我们认识了变量,这一课就来学运算符——Python 提供哪些"运算能力"。算术运算、比较大小、逻辑判断、字符串拼接……学完这一课,你就能用 Python 解决很多实际问题了。
1. 算术运算符
第02课已经简单用过几个运算符,这里把它们全部列出来,系统地看看。
| 运算符 | 名称 | 示例 | 结果 | 说明 |
|---|---|---|---|---|
+ |
加法 | 5 + 3 |
8 |
也用于字符串拼接 |
- |
减法 | 5 - 3 |
2 |
也可作负号:-5 |
* |
乘法 | 5 * 3 |
15 |
也用于字符串重复 |
/ |
除法 | 5 / 3 |
1.666... |
结果永远是浮点数 |
// |
整除 | 5 // 3 |
1 |
向下取整,去掉小数部分 |
% |
取余 | 5 % 3 |
2 |
5 ÷ 3 = 1 余 2 |
** |
幂运算 | 5 ** 3 |
125 |
5 的 3 次方 |
(1) 两个值得注意的运算符
/(除法)的诡异之处:
print(4 / 2) # 输出 2.0,而不是 2
很多人第一次看到这个结果会愣一下:4 除以 2 明明等于 2,为什么 Python 输出 2.0?因为 Python 的 / 不管能不能整除,永远返回浮点数。这在编程中是为了保持结果类型的可预期性——你看到 / 就知道结果一定是 float。
//(整除)的"向下取整"陷阱:
print(7 // 3) # 输出 2(7 ÷ 3 = 2.333,取整 = 2)
print(-7 // 3) # 输出 -3 ← 可能和你想的不一样
-7 // 3 的结果是 -3,而不是 -2。因为 Python 的 // 是向下取整(floor division),不是"去掉小数部分"。"向下"在数轴上往负无穷方向走:-2.333 向下取整是 -3。如果你要的是"向零取整"(truncation),用 int(-7 / 3),结果是 -2。
// 理解为"地板除"(floor division)比"整除"更准确,因为"整除"暗示总是去掉小数部分,但负数的情况并非如此。
▶ 示例:英寸和厘米相互换算(难度⭐)
学完算术运算符,马上用它解决一个实际生活中的问题——单位换算。
# 英寸转厘米:1 inch = 2.54 cm
# 一个标准尺子的长度
inches = 12
# 用乘法做转换
cm = inches * 2.54
# 输出结果(自动发现 cm 是浮点数)
print(f"{inches} 英寸 = {cm} 厘米")
# 反过来算:厘米 → 英寸(用除法)
cm_value = 30
inches_value = cm_value / 2.54
print(f"{cm_value} 厘米 = {inches_value:.2f} 英寸")
# 试试取余和整除——12 英寸里有多少个 5 英寸?
count = 12 // 5 # 整除:2 个
remainder = 12 % 5 # 取余:剩下 2 英寸
print(f"12 英寸可以分成 {count} 个 5 英寸,还多 {remainder} 英寸")
运行结果:
12 英寸 = 30.48 厘米
30 厘米 = 11.81 英寸
12 英寸可以分成 2 个 5 英寸,还多 2 英寸
2. 比较运算符
比较运算符用来判断两个值之间的关系,结果永远是一个布尔值(True 或 False)。它们是你写判断逻辑的基础工具。
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
== |
等于 | 5 == 3 |
False |
!= |
不等于 | 5 != 3 |
True |
> |
大于 | 5 > 3 |
True |
< |
小于 | 5 < 3 |
False |
>= |
大于等于 | 5 >= 5 |
True |
<= |
小于等于 | 5 <= 3 |
False |
age = 18
print(age == 18) # True(刚好18岁)
print(age != 18) # False(并不是"不等于18")
print(age > 21) # False(还没到美国合法饮酒年龄)
print(age >= 18) # True(恰好成年,符合大于等于的条件)
(1) 链式比较
Python 支持一种简洁的写法,在其他语言中很少见:
x = 15
print(10 < x < 20) # True(相当于 10 < x and x < 20)
print(0 < x < 10) # False
这种写法比 10 < x and x < 20 更直观,更像数学中的表达方式。
(2) is 和 is not(身份比较)
严格来说 is 不是比较运算符,但它经常和 == 放在一起对比:
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True(里面装的东西一样)
print(a is b) # False(但不是同一个盒子)
print(a is c) # True(c 只是 a 的别名,指向同一个盒子)
==比的是值:"这两个变量里装的东西是不是一样的?"is比的是身份:"这两个变量是不是指向同一个对象?"
日常开发中,99% 的场景你用 == 就够了。只有判断 None 时,惯例是用 is:if result is None:。
▶ 示例:判断一个人能不能参加百米赛跑(难度⭐)
比较运算符最常见的用途就是"把某个值和某个标准比一比"。
# 百米赛跑的分组规则
# 年龄 12~15 岁 → 少年组
# 年龄 16~20 岁 → 青年组
participant_age = 14
# 用比较运算符判断属于哪个组
is_teen_group = 12 <= participant_age <= 15 # 链式比较
is_youth_group = participant_age >= 16 and participant_age <= 20
print(f"参赛者年龄:{participant_age}")
print(f"少年组(12~15):{is_teen_group}")
print(f"青年组(16~20):{is_youth_group}")
# == 和 != 的用法
age_verified = True
print(f"年龄已验证:{age_verified == True}")
print(f"年龄未验证:{age_verified != True}")
运行结果:
参赛者年龄:14
少年组(12~15):True
青年组(16~20):False
年龄已验证:True
年龄未验证:False
3. 逻辑运算符
逻辑运算符用来组合多个条件,结果也是一个布尔值。它们在 if 判断、循环条件中无处不在。
| 运算符 | 名称 | 示例 | 结果 | 说明 |
|---|---|---|---|---|
and |
与 | True and False |
False |
两个都为真才为真 |
or |
或 | True or False |
True |
至少一个为真就为真 |
not |
非 | not True |
False |
取反 |
age = 20
has_id_card = True
# and:两个条件都满足
print(age >= 18 and has_id_card) # True(既成年又有身份证)
# or:至少满足一个
print(age >= 18 or has_id_card) # True(其实两个都满足)
# not:取反
print(not has_id_card) # False(not True = False)
(1) 真值表
如果你想彻底搞懂逻辑运算,记住这三句话就够了:
| a | b | a and b | a or b | not a |
|---|---|---|---|---|
| True | True | True | True | False |
| True | False | False | True | False |
| False | True | False | True | True |
| False | False | False | False | True |
(2) 短路求值
Python 的逻辑运算符有一个重要特性:短路求值(short-circuit evaluation)。
# 对于 and:如果左边已经是 False,右边就不执行了
def say_yes():
print("say_yes 被调用了")
return True
print(False and say_yes()) # 输出 False——say_yes 根本没被执行
print(True and say_yes()) # 输出 "say_yes 被调用了" 和 True
# 对于 or:如果左边已经是 True,右边就不执行了
print(True or say_yes()) # 输出 True——say_yes 没被执行
这个特性在实际编程中非常有用:
# 只有用户存在时,才获取他的用户名——避免 None.get() 报错
user = None
username = user and user.get("name") # user 是 None(假值),短路,username = None
print(username) # None
user = {"name": "Charlie"}
username = user and user.get("name") # user 是真值,继续执行右边,username = "Charlie"
print(username) # Charlie
user and user.get("name") 这种写法在 Python 中很常见,用短路来保证"安全访问"——左边是假值就直接返回,不再执行右边可能出错的代码。
▶ 示例:判断一个数字是否"特殊"(难度⭐⭐)
把比较运算符和逻辑运算符组合起来,判断一个数字是否满足某种"特殊条件"。
# 判断一个数字是不是"特殊数字"
# 定义:在 10~99 之间,并且是 7 的倍数或者包含数字 7
num = 49
# 条件1:在 10~99 之间(链式比较)
in_range = 10 <= num <= 99
# 条件2:是 7 的倍数(取余 + 比较)
multiple_of_7 = num % 7 == 0
# 条件3:包含数字 7(简单判断——十位是7或个位是7)
tens_digit = num // 10 # 提取十位
ones_digit = num % 10 # 提取个位
contains_7 = (tens_digit == 7) or (ones_digit == 7)
# 用逻辑运算符组合所有条件
is_special = in_range and (multiple_of_7 or contains_7)
print(f"数字:{num}")
print(f"在范围内(10~99):{in_range}")
print(f"是7的倍数:{multiple_of_7}")
print(f"包含数字7:{contains_7}")
print(f"是否特殊数字:{is_special}") # True——49是7的倍数
# 再用短路求值验证——如果第一个条件不满足,后面的根本不会执行
num2 = 200 # 不在范围内
is_special2 = (10 <= num2 <= 99) and (num2 % 7 == 0)
print(f"\n数字 {num2}:第一个条件就是 False,第二个条件不会执行——{is_special2}")
运行结果:
数字:49
在范围内(10~99):True
是7的倍数:True
包含数字7:False
是否特殊数字:True
数字 200:第一个条件就是 False,第二个条件不会执行——False
4. 赋值运算符
除了最基本的 =,Python 还提供了一组"复合赋值运算符",让你能一边运算一边赋值:
x = 10 # 基本赋值
x += 3 # 等价于 x = x + 3,结果是 13
x -= 2 # 等价于 x = x - 2,结果是 11
x *= 2 # 等价于 x = x * 2,结果是 22
x /= 4 # 等价于 x = x / 4,结果是 5.5
x //= 2 # 等价于 x = x // 2,结果是 2.0
x %= 2 # 等价于 x = x % 2,结果是 0.0
x **= 3 # 等价于 x = x ** 3,结果是 0.0
注意 x = 10 是整数,但 x /= 4 之后 x 变成了浮点数 5.5,因为 / 的结果永远是浮点数。如果你确实需要整数结果,用 //= 来避免类型变化。
# 自增在循环中的典型用法
score = 0
score += 10 # 加 10 分
score += 20 # 再加 20 分
score += 15 # 再加 15 分
print(f"最终得分:{score}") # 45
# 字符串也可以用复合赋值
message = "Hello"
message += " " # 加一个空格
message += "World"
print(message) # "Hello World"
x += 1 是"自增 1"的 Python 写法,在其他语言中写作 x++。Python 没有 ++ 运算符,用 += 1 就好。
5. 字符串运算符
字符串也有自己的"运算"——虽然不完全是数学意义上的运算,但写法上用的是同一个符号。
| 运算符 | 作用 | 示例 | 结果 |
|---|---|---|---|
+ |
拼接 | "Hello" + " " + "World" |
"Hello World" |
* |
重复 | "Ha" * 3 |
"HaHaHa" |
in |
判断包含 | "Py" in "Python" |
True |
not in |
判断不包含 | "xyz" not in "Python" |
True |
# 字符串拼接
first_name = "张"
last_name = "三"
full_name = first_name + last_name
print(full_name) # 输出 "Alice"
# 字符串重复——画分割线非常实用
line = "=" * 30
print(line) # 输出 30 个等号
# 成员判断(in / not in)
text = "Hello, Python!"
print("Python" in text) # True——"Python" 是 text 的一部分
print("Java" in text) # False——text 里没有 "Java"
print("Java" not in text) # True——确实是"不在里面"
+ 只能用于字符串和字符串之间,不能用于字符串和数字之间。"年龄:" + 25 会报错。需要先把数字转成字符串:"年龄:" + str(25)。* 只能用于字符串和整数之间——"AB" * 3 可以,"AB" * "CD" 不行。
▶ 示例:用字符串运算生成名片(难度⭐⭐)
在一个实际问题中同时用到字符串的 +、* 和 in。
# 用字符串运算生成一张简单的名片
name = "李明"
title = "Python 工程师"
company = "科技有限公司"
# 用 + 拼接名片内容
card_line1 = "姓名:" + name
card_line2 = "职位:" + title
card_line3 = "公司:" + company
# 用 * 生成分割线
separator = "-" * 25
# 组合成完整名片
business_card = separator + "\n" + card_line1 + "\n" + card_line2 + "\n" + card_line3 + "\n" + separator
print("生成的名片:")
print(business_card)
# 用 in 检查是否包含特定信息
search_term = "Python"
print(f'\n名片中是否包含 "{search_term}":{search_term in business_card}')
# 用 not in 排除某些信息
print(f'名片中不包含 "Manager":{"Manager" not in business_card}')
运行结果:
生成的名片:
-------------------------
姓名:李明
职位:Python 工程师
公司:科技有限公司
-------------------------
名片中是否包含 "Python":True
名片中不包含 "Manager":True
6. 运算符优先级
数学中有"先乘除后加减",Python 也一样——运算符有优先级高低之分。
| 优先级 | 类别 | 运算符 | 说明 |
|---|---|---|---|
| 高 | 幂运算 | ** |
右结合(从右往左算) |
| ↓ | 一元运算符 | +x、-x、not |
正负号、逻辑非 |
| ↓ | 算术运算符 | *、/、//、% |
乘、除、整除、取余 |
| ↓ | 算术运算符 | +、- |
加、减 |
| ↓ | 比较运算符 | ==、!=、>、<、>=、<= |
比较大小 |
| ↓ | 逻辑运算符 | not |
逻辑非 |
| ↓ | 逻辑运算符 | and |
逻辑与 |
| ↓ | 逻辑运算符 | or |
逻辑或 |
| 低 | 赋值运算符 | =、+= 等 |
赋值 |
# 先乘除后加减
result = 2 + 3 * 4
print(result) # 14(3*4=12,再+2)
# 比较优先级低于算术——先算完算术再比较
print(2 + 3 * 4 > 10) # True(14 > 10)
# 逻辑优先级最低——先做比较,再组合逻辑
age = 20
print(age > 18 and age < 60) # True(先算 age>18 和 age<60,再做 and)
# 幂运算是右结合
print(2 ** 3 ** 2) # 512(先算 3²=9,再算 2⁹=512)
print((2 ** 3) ** 2) # 64(加了括号,先算 2³=8,再算 8²=64)
# 括号改变优先级——跟数学一样
result = (2 + 3) * 4
print(result) # 20(先算括号内的 2+3=5,再×4)
▶ 示例:按揭月供计算器(难度⭐⭐)
公式看起来复杂,拆开看无非就是算术运算加上括号控制优先级。
# 计算房贷月供(等额本息)
# 公式:月供 = 贷款总额 × 月利率 × (1 + 月利率)^还款月数 ÷ [(1 + 月利率)^还款月数 - 1]
# 假设贷款 100 万,年利率 4.5%,贷 30 年
loan_amount = 1_000_000 # 贷款总额(用下划线分隔数字,Python 3.6+ 支持,不影响计算)
annual_rate = 0.045 # 年利率
years = 30
months = years * 12 # 还款月数
monthly_rate = annual_rate / 12 # 月利率
# 一步步拆开算——先算最复杂的部分
rate_power = (1 + monthly_rate) ** months # (1 + 月利率)^还款月数
numerator = loan_amount * monthly_rate * rate_power # 分子
denominator = rate_power - 1 # 分母
monthly_payment = numerator / denominator
print(f"贷款金额:{loan_amount:,} 元")
print(f"年利率:{annual_rate * 100}%")
print(f"贷款年限:{years} 年")
print(f"每月还款:{monthly_payment:.2f} 元")
print(f"总还款额:{monthly_payment * months:,.2f} 元")
print(f"利息总额:{monthly_payment * months - loan_amount:,.2f} 元")
运行结果:
贷款金额:1,000,000 元
年利率:4.5%
贷款年限:30 年
每月还款:5,066.85 元
总还款额:1,824,066.95 元
利息总额:824,066.95 元
1_000_000 中的下划线只是给人看的,Python 会忽略它。这是 Python 3.6 引入的数字可视化分隔符。优先级记不全没关系,拿不准就加括号——上面的公式加了括号后,任何人都能一眼看懂运算顺序。
7. 浮点数精度问题
这是每个学编程的人都会碰到的坑:
print(0.1 + 0.2) # 输出 0.30000000000000004
print(0.3) # 输出 0.3
print(0.1 + 0.2 == 0.3) # 输出 False ← 这竟然不是相等的!
这不是 Python 的 bug,而是计算机底层用二进制存储浮点数的固有限制。就像十进制无法精确表示 1/3 = 0.33333... 一样,二进制也无法精确表示 0.1。
(1) 什么时候要注意
- 做金融计算(涉及金额)——绝对不能直接用
float,用decimal模块 - 做比较——不要直接用
==比较浮点数,用差值判断
# 正确的浮点数比较方式
result = 0.1 + 0.2
epsilon = 1e-10 # 一个非常小的数,相当于 0.0000000001
print(abs(result - 0.3) < epsilon) # 输出 True
- 显示时取指定小数位——用 f-string 格式化
print(f"{0.1 + 0.2:.2f}") # 输出 0.30
# 一个实际例子:累加 0.1 十次
total = 0.0
for i in range(10):
total += 0.1
print(f"累加结果:{total}") # 0.9999999999999999
print(f"精确值:{total == 1.0}") # False
print(f"格式化后:{total:.2f}") # 1.00——显示没问题,别担心
== 直接比较浮点数"和"涉及金钱用 decimal 模块"这两条原则就够了。
8. 数字类型转换
Python 提供了几个函数来在不同数字类型之间转换:
# int → float
print(float(3)) # 输出 3.0
# float → int(直接截断小数部分,不是四舍五入)
print(int(3.14)) # 输出 3
print(int(-2.8)) # 输出 -2(向零取整)
# 字符串 → 数字(前提是字符串里确实是个数字)
print(int("42")) # 输出 42
print(float("3.14")) # 输出 3.14
# 数字 → 字符串
print(str(3.14)) # 输出 "3.14"
# int() 还可以指定进制
print(int("FF", 16)) # 输出 255(把十六进制的 FF 转成十进制)
print(int("1010", 2)) # 输出 10(把二进制的 1010 转成十进制)
▶ 示例:数据类型转换实战(难度⭐)
一个"输入→转换→计算→输出"的完整流程。
# 模拟用户输入(实际用 input(),这里用变量模拟)
user_input = "25" # 注意:这是字符串,不是数字
# 不转换就做加法会怎样?
# print(user_input + 5) ← 去掉注释会报错:不能把字符串和数字相加
# 正确做法:先用 int() 转换
age = int(user_input)
# 现在可以正常做算术了
age_next_year = age + 1
print(f"你输入了:{user_input}(类型:{type(user_input)})")
print(f"转换后:{age}(类型:{type(age)})")
print(f"明年你就 {age_next_year} 岁了")
# 反过来:把计算结果转回字符串,准备输出
output_message = "你已经 " + str(age) + " 岁了"
print(output_message)
运行结果:
你输入了:25(类型:<class 'str'>)
转换后:25(类型:<class 'int'>)
明年你就 26 岁了
你已经 25 岁了
9. 常见应用场景
- 电商价格计算:商品单价 × 数量 + 运费 - 优惠券,用 Python 表达式一行算出最终金额。满减和折扣叠加用逻辑运算符组合判断条件。
- 权限检查:判断用户是否有权操作某功能——
is_admin or (is_logged_in and has_permission)。 - 游戏伤害计算:攻击力 × 暴击倍率 - 防御力,暴击判定用比较运算符:
random.random() < crit_rate。 - 数据采样:判断一个数是不是偶数(
n % 2 == 0),或者判断是不是闰年(year % 4 == 0 and year % 100 != 0 or year % 400 == 0)。 - 时间换算:把秒数转换成"小时:分钟:秒",用整除和取余:
hours = total_seconds // 3600; minutes = (total_seconds % 3600) // 60。 - 输入验证:检查用户输入是否在有效范围内——
0 < score <= 100(链式比较),或者检查字符串包含非法字符——bad_char in username。
❓ 常见问题
Q:
==和is到底有什么区别?什么场景该用哪个? A:==比较的是值("里面装的东西一样吗"),is比较的是身份("是同一个对象吗")。比如a = [1, 2]; b = [1, 2]; c = a,则a == b是True(值一样),但a is b是False(两个不同的列表对象);a is c是True(c 只是 a 的别名)。日常开发中,99% 的场景用==就够了。判断是否为None时,惯例用is:if result is None:。
Q:
and和or的短路求值在实际中有什么坑? A: 最常见的坑是:user_input = input() or "默认值"——如果input()返回空字符串(假值),or会短路直接取右边的默认值。这个写法很常见,但新手容易误读。另一个陷阱是函数调用放and右边——如果左边是假值,右边函数不会执行,你可能意识不到某些操作被跳过了。好习惯是:如果右边的表达式有副作用(比如文件写入、数据库更新),不要依赖短路行为,分开写。
Q:
/和//到底什么时候用哪个? A: 如果你需要精确的带小数点的结果(比如 7 ÷ 3 = 2.333...),用/。如果你只需要整数结果(比如把 7 个苹果分给 3 个人,每人分 2 个,剩下 1 个),用//配合%。如果你是新手且不确定,默认用/,至少不会丢掉精度。明确需要整数后再改成//。
Q:幂运算符的右结合是什么意思?为什么这样设计? A: 右结合意味着
2 3 2被解析为2 (3 2),即先算3² = 9,再算2⁹ = 512。如果从左到右算就是(2³)² = 8² = 64,结果完全不同。这和数学中的习惯一致:a^b^c的标准解释是从右上角开始算。你可以通过加括号来强制顺序:(2 3) 2得到 64。
📖 小节
- 算术运算符 7 个:
+-*///%**;/永远返回浮点数,//是向下取整 - 比较运算符 6 个:
==!=><>=<=;Python 支持链式比较如10 < x < 20 - 逻辑运算符 3 个:
and(全真才真)、or(一真即真)、not(取反);它们都被短路求值 - 字符串运算符:
+拼接、*重复、in/not in判断是否包含 - 运算优先级:
**> 一元 > 算术 > 比较 >not>and>or> 赋值。拿不准就加括号 - 浮点数无法用
==精确比较,应使用差值法abs(a - b) < epsilon
📝 作业
-
基础题(难度⭐):定义三个变量
a=10、b=3、c=20,分别计算并输出:a + b * c、(a + b) * c、a ** b、a // b、c % a、a > b and c > 15。先猜结果,再运行验证。 -
进阶题(难度⭐⭐):给定年份
year = 2026,判断它是不是闰年。闰年规则:能被 4 整除但不能被 100 整除,或者能被 400 整除。提示:year % 4 == 0判断能否被 4 整除。 -
挑战题(难度⭐⭐⭐):写一个简单的"温控系统"。给定
current_temp = 38、high_alarm = 37、low_alarm = 5。如果当前温度超过高温线或低于低温线,触发警报。如果当前温度在警戒线边缘(±2 度以内),触发"注意"而非"警报"。要求只用算术、比较和逻辑运算符实现完整的判断逻辑。提示:abs(current_temp - high_alarm) <= 2判断是否在高温警戒边缘。