02-运算符
本章代码基于 Python 3.11+ 编写
运算符是程序进行计算和判断的基础,掌握运算符让程序能够处理数据。
本章讲解 Python 的算术运算符、比较运算符和逻辑运算符。
概念铺垫
为什么需要运算符?一个真实的计算场景
问题场景: 你在开发一个购物计算程序,需要计算总价、折扣、税费。
不使用运算符的困惑:
- 如何在程序中进行数学计算?
- 如何判断价格是否达到折扣门槛?
- 如何组合多个条件?
使用运算符的解决方案:
python
# 购物计算
price: float = 100.0
quantity: int = 3
# 算术运算符:计算总价
total: float = price * quantity
# 比较运算符:判断是否享受折扣
has_discount: bool = total >= 200
# 逻辑运算符:组合多个条件
is_member: bool = True
final_discount: float = 0.9 if (has_discount and is_member) else 1.0
final_price: float = total * final_discount
print(f"原价:{total}元,折后:{final_price}元")这就是运算符的价值:让程序能够进行计算和判断。
运算符解决了什么问题?
运算符的本质是:让数据之间产生关系,进行计算或比较。
就像你用计算器按加减乘除,Python 用运算符处理数据。
运算符的类型:
- 算术运算符:进行数学计算(+、-、*、/)
- 比较运算符:比较大小关系(>、<、==)
- 逻辑运算符:组合条件(and、or、not)
- 赋值运算符:给变量赋值(=、+=、-=)
运算符的最简用法
最常用的运算符只有4个:加、减、乘、除。
python
# 算术运算
print(10 + 5) # 加法:15
print(10 - 5) # 减法:5
print(10 * 5) # 乘法:50
print(10 / 5) # 除法:2.0
# 比较运算
print(10 > 5) # 大于:True
print(10 == 5) # 等于:False
# 逻辑运算
print(True and False) # 与:False
print(True or False) # 或:True这就是运算符的基本用法。接下来我们详细学习各类运算符。
什么是运算符
运算符(Operator) 是用于执行特定运算的符号。
┌─────────────────────────────────────────┐
│ 运算示意图 │
├─────────────────────────────────────────┤
│ │
│ 操作数 运算符 操作数 = 结果 │
│ │ │ │ │ │
│ 3 + 5 8 │
│ │
└─────────────────────────────────────────┘L1 理解层:会用
算术运算符
运算符一览
┌──────────┬──────────────────────────────┐
│ 运算符 │ 说明 │
├──────────┼──────────────────────────────┤
│ + │ 加法 │
│ - │ 减法 │
│ * │ 乘法 │
│ / │ 除法(结果总是浮点数) │
│ // │ 整除(向下取整) │
│ % │ 取模(取余数) │
│ ** │ 幂运算(乘方) │
└──────────┴──────────────────────────────┘示例代码
python
# 加减乘除
print(3 + 5) # 8
print(10 - 3) # 7
print(4 * 5) # 20
print(10 / 2) # 5.0(注意是浮点数)
# 整除和取模
print(10 // 3) # 3(商)
print(10 % 3) # 1(余数)
# 幂运算
print(2 ** 3) # 8(2 的 3 次方)
print(4 ** 0.5) # 2.0(平方根)整除说明(核心)
整除 //: 向下取整(朝负无穷方向)。
整除规则:
┌─────────────────────────────────────────────────────────────┐
│ 整除 // 向下取整 │
│ │
│ 10 // 3 = 3 (正数:正常除法取整) │
│ -10 // 3 = -4 (负数:向下取整,不是 -3!) │
│ │
│ ⚠️ 注意:负数整除不是简单去掉小数 │
│ │
│ 图解: │
│ 数轴:... -5 -4 -3 -2 -1 0 1 2 3 ... │
│ ↑ ↑ │
│ -3.33 取整 3.33 取整 │
│ 结果为 -4 结果为 3 │
│ (向下) (向下) │
└─────────────────────────────────────────────────────────────┘最简示例
python
print(10 // 3) # 3
print(-10 // 3) # -4(注意不是 -3)关键代码解释
| 表达式 | 计算过程 | 结果 |
|---|---|---|
10 // 3 | 10 / 3 = 3.33 → 向下取整 | 3 |
-10 // 3 | -10 / 3 = -3.33 → 向下取整 | -4 |
取模应用
取模 %: 获取除法的余数。
取模应用场景:
┌─────────────────────────────────────────────────────────────┐
│ 取模 % 的常见用途 │
│ │
│ num % 2 判断奇偶(0=偶,1=奇) │
│ num % 10 获取最后一位数字 │
│ num % 60 分钟转秒(循环计数) │
│ num % n 循环索引(0到n-1) │
└─────────────────────────────────────────────────────────────┘最简示例
python
num = 7
print(num % 2) # 1(奇数)关键代码解释
| 表达式 | 含义 | 结果 |
|---|---|---|
num % 2 == 0 | 奇偶判断 | True 为偶数 |
num % 10 | 最后一位 | 1234 % 10 = 4 |
详细示例
python
# 判断奇偶数
num = 7
if num % 2 == 0:
print("偶数")
else:
print("奇数")
# 获取最后一位数字
num = 1234
print(num % 10) # 4运算符优先级
优先级规则
优先级: 决定运算的执行顺序。
优先级结构图:
┌─────────────────────────────────────────────────────────────┐
│ 算术运算符优先级(从高到低) │
│ │
│ 优先级 运算符 示例 │
│ ───────────────────────────────────── │
│ 最高 () (2 + 3) * 4 → 括号先算 │
│ 第二 ** 2 ** 3 = 8 → 幂运算 │
│ 第三 * / // % 2 * 3 = 6 → 乘除类 │
│ 最低 + - 2 + 3 = 5 → 加减 │
│ │
│ 记忆口诀:先括号,再乘方,接着乘除,最后加减 │
│ │
│ ⚠️ 建议:复杂表达式用括号明确顺序 │
└─────────────────────────────────────────────────────────────┘最简示例
python
print(2 + 3 * 4) # 14(先乘后加)关键代码解释
| 表达式 | 执行顺序 | 结果 |
|---|---|---|
2 + 3 * 4 | 先 3*4=12,再 2+12 | 14 |
(2 + 3) * 4 | 先 2+3=5,再 5*4 | 20 |
2 * 3 ** 2 | 先 3**2=9,再 2*9 | 18 |
比较运算符
运算符一览
┌──────────┬──────────────────────────────┐
│ 运算符 │ 说明 │
├──────────┼──────────────────────────────┤
│ == │ 等于 │
│ != │ 不等于 │
│ > │ 大于 │
│ < │ 小于 │
│ >= │ 大于等于 │
│ <= │ 小于等于 │
└──────────┴──────────────────────────────┘
注意:比较运算的结果是布尔值(True 或 False)示例
python
# 数字比较
print(5 == 5) # True
print(5 != 3) # True
print(5 > 3) # True
print(5 < 3) # False
# 字符串比较(字典序)
print("apple" < "banana") # True
# 链式比较
x = 5
print(1 < x < 10) # True重要区别
python
# = 赋值运算符
x = 5 # 把 5 赋值给 x
# == 比较运算符
x == 5 # 检查 x 是否等于 5逻辑运算符
运算符一览
┌──────────┬──────────────────────────────┐
│ 运算符 │ 说明 │
├──────────┼──────────────────────────────┤
│ and │ 与:两边都为真才为真 │
│ or │ 或:有一边为真就为真 │
│ not │ 非:取反 │
└──────────┴──────────────────────────────┘真值表
and 运算:
True and True = True
True and False = False
False and True = False
False and False = False
or 运算:
True or True = True
True or False = True
False or True = True
False or False = False
not 运算:
not True = False
not False = True示例
python
# and:两个条件都要满足
age = 20
has_money = True
can_buy = age >= 18 and has_money
print(can_buy) # True
# or:只要满足一个条件
day = "Saturday"
is_weekend = day == "Saturday" or day == "Sunday"
print(is_weekend) # True
# not:取反
is_raining = False
can_go_out = not is_raining
print(can_go_out) # True短路求值
python
# and 短路:第一个为 False,不计算第二个
print(False and print("不打印")) # False
# or 短路:第一个为 True,不计算第二个
print(True or print("不打印")) # True真值判断(核心)
什么是真值判断
Python 在布尔上下文中会自动将任意值转换为 True 或 False。
┌─────────────────────────────────────────────────────────────┐
│ 布尔上下文(自动进行真值判断的地方) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. if 条件 │
│ if x: ← x 自动转换为 True/False │
│ │
│ 2. while 条件 │
│ while x: ← x 自动转换为 True/False │
│ │
│ 3. 逻辑运算 │
│ x and y ← x 和 y 自动转换 │
│ x or y ← x 和 y 自动转换 │
│ not x ← x 自动转换后取反 │
│ │
│ 4. 三元表达式 │
│ a if x else b ← x 自动转换 │
│ │
│ 核心概念: │
│ • bool(x) 函数返回 x 的真值 │
│ • Python 不要求 x 是布尔类型,任意值都可以判断 │
│ │
└─────────────────────────────────────────────────────────────┘假值完整列表
Python 中以下值在布尔上下文中被视为 False:
┌─────────────────────────────────────────────────────────────┐
│ 假值(Falsy)完整列表 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 类型 假值示例 说明 │
│ ───────────────────────────────────────────── │
│ 常量 None 空值 │
│ 常量 False 布尔假 │
│ │
│ 数值 0 整数零 │
│ 数值 0.0 浮点零 │
│ 数值 0j 复数零 │
│ │
│ 字符串 '' 空字符串 │
│ 字符串 "" 空字符串 │
│ 字符串 b'' 空字节串 │
│ │
│ 容器 () 空元组 │
│ 容器 [] 空列表 │
│ 容器 {} 空字典 │
│ 容器 set() 空集合 │
│ 容器 range(0) 空范围 │
│ │
│ ⚠️ 重要: │
│ • 空容器 ≠ None(但都视为 False) │
│ • 0 ≠ False(但都视为 False) │
│ • '' ≠ 空(但都视为 False) │
│ │
│ ✅ 其他所有值都视为 True │
│ │
└─────────────────────────────────────────────────────────────┘最简示例
python
# 查看各种值的真值
print(bool(None)) # False
print(bool(False)) # False
print(bool(0)) # False
print(bool(0.0)) # False
print(bool('')) # False
print(bool([])) # False
print(bool({})) # False
# 非假值都是 True
print(bool(1)) # True
print(bool('hello')) # True
print(bool([1, 2])) # True
print(bool({'a': 1})) # True关键代码解释
| 表达式 | 真值 | 原因 |
|---|---|---|
bool(None) | False | None 是空值 |
bool(0) | False | 数值零是假值 |
bool('') | False | 空字符串是假值 |
bool([]) | False | 空列表是假值 |
bool([1]) | True | 非空列表是真值 |
bool('a') | True | 非空字符串是真值 |
真值判断的惯用写法
原则:用真值判断而非显式比较,代码更简洁。
python
# ❌ 不推荐的写法(冗长)
if items == []:
...
if items != []:
...
if name == '':
...
if name != '':
...
if value == None:
...
if value != None:
...
# ✅ 推荐的惯用写法(简洁)
if not items: # 空列表
...
if items: # 非空列表
...
if not name: # 空字符串
...
if name: # 非字符串
...
if value is None: # None 检查(用 is,不用 ==)
...
if value is not None: # 非 None
...对比说明:
| 写法 | 等价于 | 推荐 | 原因 |
|---|---|---|---|
if not items: | if items == []: | ✅ | 惯用法,简洁 |
if items: | if items != []: | ✅ | 惯用法,简洁 |
if not name: | if name == '': | ✅ | 惯用法,简洁 |
if value is None: | if value == None: | ✅ | None 用 is 判断身份 |
if value == None: | — | ❌ | 可能有意外行为 |
⚠️ None 的特殊处理:
python
# None 检查用 is,不用 == 或 not
value = None
# ✅ 正确:用 is 检查身份
if value is None:
print("值是 None")
# ✅ 正确:用 is not 检查非 None
if value is not None:
print("值不是 None")
# ❌ 不推荐:用 == 检查
if value == None: # 虽然结果正确,但语义不对
...
# ⚠️ 注意:not value 无法区分 None 和其他假值
value = None
name = ''
items = []
# 这三种情况 not 都返回 True
print(not value) # True(None)
print(not name) # True(空字符串)
print(not items) # True(空列表)
# 所以检查 None 要用 is None真值判断的实际应用
示例:栈的空检查
python
class Stack:
def __init__(self) -> None:
self._items: list[int] = []
def push(self, item: int) -> None:
self._items.append(item)
def pop(self) -> int:
# ✅ 惯用写法:用 not 检查空列表
if not self._items:
raise IndexError("栈为空")
return self._items.pop()
def is_empty(self) -> bool:
# ✅ 惯用写法:直接返回真值(取反)
return not self._items
# 使用
stack = Stack()
stack.push(1)
stack.push(2)
print(stack.pop()) # 2
print(stack.is_empty()) # False
print(stack.pop()) # 1
print(stack.is_empty()) # True示例:用户输入检查
python
def greet(name: str) -> str:
# ✅ 惯用写法:检查空字符串
if not name:
return "请输入姓名"
return f"你好,{name}"
print(greet("")) # "请输入姓名"
print(greet("张三")) # "你好,张三"示例:可选参数检查
python
def process(data: list[int] | None) -> int:
# ✅ 惯用写法:区分 None 和空列表
if data is None:
return 0 # 无数据
if not data: # 空列表
return -1 # 数据为空
return sum(data)
print(process(None)) # 0(无数据)
print(process([])) # -1(数据为空)
print(process([1, 2])) # 3(有数据)常见误区
误区1:认为 not 只对布尔值有效
python
# ❌ 误解:not 只用于 True/False
is_valid = True
if not is_valid: # 这个理解正确
...
# ✅ 正解:not 对任意值有效
items = []
if not items: # items 是列表,但 not 可以判断真值
...误区2:混淆假值类型
python
# ❌ 误解:空列表和 None 是同一回事
if value == []: # 这只匹配空列表,不匹配 None
...
# ✅ 正解:它们都是假值,但类型不同
value = None
print(value == []) # False(类型不同)
print(not value) # True(都是假值)
print(bool(value)) # False
print(bool([])) # False误区3:用 not 检查 None
python
# ❌ 不推荐:无法区分 None 和其他假值
def process(value: str | None) -> str:
if not value: # None 和 '' 都会进入这里
return "无值"
return value
# ✅ 正确:明确区分
def process(value: str | None) -> str:
if value is None:
return "无数据"
if not value: # 空字符串
return "空字符串"
return value从简单到复杂:运算符的渐进应用
层级1:基础计算
python
# 简单的四则运算
a: int = 10
b: int = 3
print(a + b) # 加:13
print(a - b) # 减:7
print(a * b) # 乘:30
print(a / b) # 除:3.333...层级2:高级算术
python
# 整除、取模、幂运算
a: int = 10
b: int = 3
print(a // b) # 整除:3
print(a % b) # 取模:1
print(a ** b) # 幂:1000层级3:比较判断
python
# 判断奇偶
num: int = 7
is_odd: bool = num % 2 != 0
print(f"{num}是奇数:{is_odd}")
# 范围判断
score: int = 85
is_pass: bool = 60 <= score <= 100
print(f"成绩有效:{is_pass}")层级4:逻辑组合
python
# 多条件判断
age: int = 25
has_license: bool = True
can_drive: bool = age >= 18 and has_license
print(f"可以驾驶:{can_drive}")
# 会员优惠
is_member: bool = True
total: float = 150.0
has_discount: bool = is_member or total >= 200
print(f"享受折扣:{has_discount}")层级5:复合运算
python
# 购物车计算
prices: list[float] = [29.9, 15.5, 8.0]
quantities: list[int] = [2, 1, 3]
# 计算总价
total: float = sum(p * q for p, q in zip(prices, quantities))
# 满减优惠
discount: float = 20.0 if total >= 100 else 0.0
final: float = total - discount
print(f"商品总价:{total:.1f}元")
print(f"满减优惠:{discount:.1f}元")
print(f"实付金额:{final:.1f}元")综合应用:购物车结算程序
这个示例综合运用各类运算符:
python
# 购物车结算程序(Python 3.11+)
from typing import Any
def calculate_total(cart: list[dict[str, Any]]) -> dict[str, float]:
"""
计算购物车总价
Args:
cart: 购物车商品列表
Returns:
结算信息字典
"""
# 计算商品总价
subtotal: float = sum(item["price"] * item["quantity"] for item in cart)
# 判断是否满减(算术 + 比较)
discount: float = 0.0
if subtotal >= 200:
discount = 30.0
elif subtotal >= 100:
discount = 15.0
# 会员折扣(逻辑运算符)
is_member: bool = True
member_discount: float = 0.95 if is_member else 1.0
# 计算最终价格
after_discount: float = (subtotal - discount) * member_discount
return {
"subtotal": subtotal,
"discount": discount,
"member_rate": member_discount,
"final": after_discount
}
# 使用示例
cart: list[dict[str, Any]] = [
{"name": "Python书籍", "price": 59.9, "quantity": 2},
{"name": "编程键盘", "price": 199.0, "quantity": 1},
{"name": "鼠标垫", "price": 29.9, "quantity": 1}
]
result: dict[str, float] = calculate_total(cart)
print("=== 购物车结算 ===")
print(f"商品总价:{result['subtotal']:.2f}元")
print(f"满减优惠:{result['discount']:.2f}元")
print(f"会员折扣:{result['member_rate']:.0%}")
print(f"实付金额:{result['final']:.2f}元")这个程序展示了:
- 算术运算符:计算价格和折扣
- 比较运算符:判断满减条件
- 逻辑运算符:会员资格判断
- 运算符优先级:括号改变计算顺序
- 类型提示的现代语法
关键代码说明:
| 代码 | 含义 | 为什么这样写 |
|---|---|---|
sum(item["price"] * item["quantity"] for item in cart) | 用生成器表达式计算所有商品的价格×数量之和 | 一行完成循环乘法累加,比写 for 循环更简洁 |
subtotal >= 200 | 比较运算:判断总价是否达到满减门槛 | 比较运算结果为布尔值,用于控制 if 分支 |
0.95 if is_member else 1.0 | 三元运算符:会员打 95 折,非会员不打折 | 比 if-else 四行写法更简洁,适合简单二选一 |
(subtotal - discount) * member_discount | 括号提升优先级:先减满减,再乘折扣 | 不加括号会先乘再减,计算结果不同 |
result['subtotal']:.2f | f-string 访问字典键并格式化为两位小数 | :2f 控制输出精度,避免浮点数显示过长 |
L2 实践层:用好
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 复杂表达式加括号明确顺序 | 可读性优先,不依赖记忆优先级 | (a + b) * c |
| 用链式比较替代 and | 更自然、更 Pythonic | 0 < x < 100 |
用 not items 检查空容器 | 惯用法,比 len(items) == 0 简洁 | if not items: |
用 is None 判断空值 | None 是单例,is 检查身份 | if value is None: |
| 利用短路求值做防御性编程 | 避免在空对象上调用方法 | if data and data.get("key"): |
用 and/or 的真假短路做默认值 | 简洁的默认值写法(谨慎使用) | name = user_input or "匿名" |
比较数值用 ==,不用 is | is 比较身份,不可靠 | if x == 100: |
用 // 和 % 做循环计数 | 整除+取模是周期问题的标准解法 | i // n 第几组,i % n 组内位置 |
反模式:不要这样做
python
# ❌ 错误:复杂表达式不加括号,依赖记忆优先级
result = a + b * c ** d // e % f # 谁知道先算什么?
# ✅ 正确:加括号明确意图
result = a + (b * (c ** d) // e) % f
# 或拆分为多步:
temp = b * (c ** d)
result = a + (temp // e) % fpython
# ❌ 错误:用 == 比较浮点数
x = 0.1 + 0.2
if x == 0.3:
print("相等") # 永远不会执行!
# ✅ 正确:用误差范围比较
import math
if math.isclose(x, 0.3):
print("近似相等")python
# ❌ 错误:用 len() 检查空容器
if len(items) == 0:
...
# ✅ 正确:用真值判断惯用法
if not items:
...python
# ❌ 错误:用 is 比较数值
if x is 100:
...
# ✅ 正确:用 == 比较数值
if x == 100:
...python
# ❌ 错误:混淆 = 和 ==
if x = 5: # SyntaxError
...
# ✅ 正确:赋值用 =,比较用 ==
x = 5
if x == 5:
...python
# ❌ 错误:and/or 短路误用(空字符串被当作假值)
name = user_input or "匿名" # 如果用户输入空字符串,也会被替换
# 问题:空串 '' 和 None 都会被替换成 "匿名"
# ✅ 正确:明确只处理 None
name = user_input if user_input is not None else "匿名"
# 或用三元表达式
name = "匿名" if user_input is None else user_input常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 浮点数比较 | 0.1 + 0.2 != 0.3 | 用 math.isclose() |
| 整除负数方向 | -10 // 3 = -4 不是 -3 | 记住是向下取整 |
| 短路求值副作用 | cond and expensive_func() 中 expensive_func 可能不执行 | 不应依赖短路求值触发副作用 |
and/or 返回值 | x and y 返回的是值(不是 True/False) | 需要布尔值时包装 bool(x and y) |
not 优先级高于 and/or | not a and b = (not a) and b | 不明确时加括号 |
| 除数为 0 | 10 / 0 抛 ZeroDivisionError | 先检查 if b != 0 |
适用场景
| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 数字计算 | ✅ 使用算术运算符 | 基础功能 |
| 条件判断 | ✅ 使用比较运算符 | 基础功能 |
| 多条件组合 | ✅ 使用逻辑运算符 | 简洁清晰 |
| 范围判断 | ✅ 使用链式比较 0 < x < 100 | 比 x > 0 and x < 100 更自然 |
| 空值检查 | ✅ 用 is None / not items | 惯用法,简洁安全 |
| 浮点数科学计算 | ❌ 用 decimal.Decimal | 避免精度误差 |
| 位运算 | ❓ 使用 `& | ^ ~` |
| 矩阵运算 | ❌ 用 NumPy | 专业库提供更好的性能和功能 |
| 三态逻辑(含 None) | ❌ 用显式判断 | and/or 对 None 行为可能反直觉 |
L3 专家层:深入
Python 如何实现运算符
短路求值的内部机制
短路求值字节码分析:
┌─────────────────────────────────────────────────────────────┐
│ and / or 短路求值的实现 │
├─────────────────────────────────────────────────────────────┤
│ │
│ x and y 的执行语义: │
│ ────────────── │
│ if x is False: │
│ return x ← 短路:不计算 y │
│ else: │
│ return y ← 返回 y 的值(非布尔化) │
│ │
│ x or y 的执行语义: │
│ ───────────── │
│ if x is True: │
│ return x ← 短路:不计算 y │
│ else: │
│ return y ← 返回 y 的值(非布尔化) │
│ │
│ ⚠️ 关键:and/or 返回的是"值",不是 True/False! │
│ 0 and 5 → 0 (返回第一个假值) │
│ 3 and 5 → 5 (返回最后一个真值) │
│ 0 or 5 → 5 (返回第一个真值) │
│ 3 or 5 → 3 (返回第一个真值) │
│ │
└─────────────────────────────────────────────────────────────┘验证代码:
python
# and/or 返回值的证明
print(0 and 5) # 0(返回第一个假值)
print(3 and 5) # 5(返回最后一个值)
print(0 or 5) # 5(返回第一个真值)
print(3 or 5) # 3(返回第一个真值)
# 短路求值验证
def side_effect():
print("执行了!")
return True
print(False and side_effect()) # 不输出 "执行了!"
print(True or side_effect()) # 不输出 "执行了!"
# 查看 and/or 的字节码
from dis import dis
dis("a and b")
# 关键指令:
# JUMP_IF_FALSE_OR_POP → 如果 a 为假则跳转(短路)
dis("a or b")
# 关键指令:
# JUMP_IF_TRUE_OR_POP → 如果 a 为真则跳转(短路)整除(//)的内部实现
整除实现的数学原理:
┌─────────────────────────────────────────────────────────────┐
│ 整除 // 的内部机制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ a // b 等价于 floor(a / b) │
│ │
│ floor(x) = 不大于 x 的最大整数 │
│ │
│ 正向: │
│ 10 / 3 = 3.333... → floor(3.333) = 3 → 10 // 3 = 3 │
│ │
│ 负向: │
│ -10 / 3 = -3.333... → floor(-3.333) = -4 → -10 // 3 = -4 │
│ │
│ 除数和余数的关系(Python 保证): │
│ a = b * (a // b) + (a % b) │
│ │
│ 验证: │
│ 10 = 3 * (10 // 3) + (10 % 3) = 3 * 3 + 1 = 9 + 1 = 10 ✓ │
│ -10 = 3 * (-10 // 3) + (-10 % 3) = 3 * (-4) + 2 = -12+2 ✓│
│ │
│ 注意:不同语言对负数取模的结果不同: │
│ Python: -10 % 3 = 2(余数非负) │
│ C/Java: -10 % 3 = -1(余数与被除数同号) │
│ │
└─────────────────────────────────────────────────────────────┘验证代码:
python
# 整除和取模的一致性
a, b = -10, 3
print(a == b * (a // b) + (a % b)) # True(Python 的数学保证)
# 对比不同语言的余数
print(-10 % 3) # 2(Python:余数非负)
# C/Java:结果是 -1
# 查看字节码
from dis import dis
dis("10 // 3")
# BINARY_FLOOR_DIVIDE
dis("10 % 3")
# BINARY_MODULO运算符重载协议
运算符的魔术方法映射:
┌─────────────────────────────────────────────────────────────┐
│ 运算符与魔术方法的对应关系 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 运算符 魔术方法 运算符 魔术方法 │
│ ────── ──────── ────── ──────── │
│ + __add__ == __eq__ │
│ - __sub__ != __ne__ │
│ * __mul__ < __lt__ │
│ / __truediv__ > __gt__ │
│ // __floordiv__ <= __le__ │
│ % __mod__ >= __ge__ │
│ ** __pow__ and 无法重载 │
│ │
│ ⚠️ and/or/not 不能被重载!(它们是关键字,不是运算符方法) │
│ │
│ 链式比较的实现: │
│ a < b < c 编译为 a < b and b < c(但 b 只计算一次) │
│ │
└─────────────────────────────────────────────────────────────┘验证代码:
python
# 运算符重载示例
class Vector:
def __init__(self, x: int, y: int) -> None:
self.x = x
self.y = y
def __add__(self, other: "Vector") -> "Vector":
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Vector):
return NotImplemented
return self.x == other.x and self.y == other.y
def __repr__(self) -> str:
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v1 == v2) # False
# 查看链式比较的字节码
from dis import dis
dis("a < b < c")
# 可以看到 b 只被 LOAD 一次,但比较两次性能考量
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
+, -, *, / (整数) | O(1) ~ O(n) | 小整数 O(1),大整数随位数增长 |
+, -, *, / (浮点) | O(1) | IEEE 754 硬件指令 |
** (幂运算) | O(log n) | 快速幂算法 |
//, % | O(1) ~ O(n) | 同除法复杂度 |
比较 ==, <, > | O(1) ~ O(n) | 字符串比较 O(min(len(a), len(b))) |
and/or | O(1) | 最多计算两个操作数 |
not | O(1) | 单操作数取反 |
链式比较 a < b < c | O(1) | 两个比较,b 只计算一次 |
python
import timeit
# 幂运算 vs 乘法
print(timeit.timeit("2 ** 100", number=1_000_000))
# → ~0.03 秒
print(timeit.timeit("2 * 2 * 2 * 2 * 2", number=1_000_000))
# → ~0.02 秒(少量乘法更快,但幂运算在大指数时显著更快)
# 链式比较 vs 两次比较
print(timeit.timeit("0 < x < 100", setup="x = 50", number=10_000_000))
# → ~0.3 秒
print(timeit.timeit("0 < x and x < 100", setup="x = 50", number=10_000_000))
# → ~0.4 秒(链式比较略快,因为 x 只计算一次)知识关联
运算符知识关联:
┌─────────────────────┐
│ Python 运算符 │
└─────────────────────┘
│
┌─────────┼─────────┬─────────┐
↓ ↓ ↓ ↓
┌────────┐ ┌────────┐ ┌────────┐ ┌────────────┐
│ 算术 │ │ 比较 │ │ 逻辑 │ │ 位运算 │
│+ - * / │ │== != <> │ │and or │ │& | ^ ~ │
│// % ** │ │>= <= │ │not │ │<< >> │
└────────┘ └────────┘ └────────┘ └────────────┘
│ │ │
↓ ↓ ↓
┌──────────────────────────────────────────┐
│ 魔术方法协议(__add__ 等) │
│ 运算符可被自定义类重载 │
└──────────────────────────────────────────┘
│
↓
┌─────────────────────┐
│ 短路求值 │
│ JUMP_IF_FALSE_OR │
│ JUMP_IF_TRUE_OR │
└─────────────────────┘
│
↓
┌─────────────────────┐ ┌─────────────────────┐
│ 真值判断协议 │────→│ 流程控制(if/while)│
│ __bool__ > __len__│ │ 基于真值执行分支 │
└─────────────────────┘ └─────────────────────┘设计动机
| 设计选择 | 原因 | 影响 |
|---|---|---|
and/or 返回值而非布尔值 | 灵活用于默认值模式 name = input or "匿名" | 同时带来陷阱(空字符串也是假值) |
Python 的 -10 % 3 = 2(非负) | 数学上更一致:a = b*q + r 且 `0 ≤ r < | b |
// 向下取整而非截断 | 保证 a == b*(a//b) + a%b 恒成立 | 负数场景需要适应 |
链式比较 a < b < c | 更接近数学写法 | b 只计算一次,性能略优 |
本章小结
┌─────────────────────────────────────────────────────────────┐
│ 运算符 知识要点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 算术运算符: │
│ ✓ + - * / // % ** │
│ ✓ / 结果总是浮点数 │
│ │
│ 比较运算符: │
│ ✓ == != > < >= <= │
│ ✓ 结果是布尔值 │
│ │
│ 逻辑运算符: │
│ ✓ and or not │
│ ✓ 支持短路求值 │
│ │
│ 真值判断(核心): │
│ ✓ 假值:None, False, 0, '', [], {}, set() │
│ ✓ 惯用写法:if not items:(检查空) │
│ ✓ None 检查:if value is None:(用 is) │
│ ✓ 区分 None 和空容器 │
│ │
│ 优先级: │
│ ✓ () > ** > * / // % > + - │
│ │
│ 实际应用: │
│ ✓ 价格计算、条件判断、优惠规则 │
│ ✓ 空值检查、栈空判断 │
│ │
└─────────────────────────────────────────────────────────────┘