什么是运算符?
简单来说,运算符就是告诉 Python,要对操作数做什么运算的「指令」,而操作数就是运算的「原材料」。
比如这行简单代码:

1
result = 1 + 3

在这行代码里:

  • 13操作数,也就是我们用来做运算的原材料
  • +算术运算符,告诉 Python 要把两个操作数加起来
  • =赋值运算符,告诉 Python 要把运算的结果,存到变量result

运算符都有哪些呢?接下来给它们分分类,我们逐个来看:

1. 算术运算符


运算符 名称 示例 结果 说明
+ 加法 3 + 5 8 数字相加、字符串拼接
- 减法 10 - 4 6 数字相减
* 乘法 2 * 6 12 数字相乘、字符串重复
/ 除法 7 / 2 3.5 结果为浮点数
// 整除 7 // 2 3 向下取整,-7//2 = -4
% 取余 7 % 2 1 求余数,符号与除数一致
** 幂运算 2 ** 3 8 次方,也可开平方 9**0.5=3.0
+ 正号 +5 5 保持原值
- 负号 -5 -5 取反
  1. 加法 +
1
2
3
4
# 整数加法
print(5 + 3) # 输出:8
# 浮点数加法
print(2.5 + 1.5) # 输出:4.0
  1. 减法 -
1
2
print(10 - 4)         # 输出:6
print(3 - 5) # 输出:-2(支持负数结果)
  1. 乘法 *
1
2
print(4 * 5)          # 输出:20
print(2.5 * 2) # 输出:5.0
  1. 普通除法 /
1
2
3
# Python3中,/ 永远返回浮点数,无论操作数是不是整数
print(10 / 3) # 输出:3.3333333333333335
print(6 / 2) # 输出:3.0(即使能整除,也返回浮点数)
  1. 整除 //
1
2
3
# 向下取整的整除
print(10 // 3) # 输出:3(整数操作返回整数)
print(10.5 // 2) # 输出:5.0(浮点数操作返回浮点数)
  1. 取模(取余数)%
1
2
3
# 取除法的余数
print(10 % 3) # 输出:1
print(10.5 % 3) # 输出:1.5(浮点数也支持取模)
  1. 幂运算 **
1
2
3
4
# 计算a的b次方
print(2 ** 3) # 输出:8(2的3次方)
print(2 ** -1) # 输出:0.5(Python3支持负指数,等价于1/2)
print(9 ** 0.5) # 输出:3.0(也可以用来开平方)

2. 赋值运算符


运算符 名称 示例 等价于 说明
= 赋值 a = 5 a = 5 基本赋值
+= 加法赋值 a += 3 a = a + 3 复合赋值
-= 减法赋值 a -= 2 a = a - 2 复合赋值
*= 乘法赋值 a *= 4 a = a * 4 复合赋值
/= 除法赋值 a /= 2 a = a / 2 复合赋值
//= 整除赋值 a //= 2 a = a // 2 复合赋值
%= 取余赋值 a %= 3 a = a % 3 复合赋值
**= 幂赋值 a **= 2 a = a ** 2 复合赋值
  1. 基础赋值 =
    最基础的赋值,把右边的值绑定到左边的变量
1
2
a = 5
print(a) # 输出:5
  1. 加法赋值 +=
    等价于 a = a + 右边的值,简化写法
1
2
3
a = 5
a += 3
print(a) # 输出:8
  1. 减法赋值 -=
    等价于 a = a - 右边的值
1
2
3
a = 10
a -= 4
print(a) # 输出:6
  1. 乘法赋值 *=
    等价于 a = a * 右边的值
1
2
3
a = 4
a *= 2
print(a) # 输出:8
  1. 除法赋值 /=
    等价于 a = a / 右边的值,Python3 里永远返回浮点数
1
2
3
a = 10
a /= 2
print(a) # 输出:5.0
  1. 整除赋值 //=
    等价于 a = a // 右边的值
1
2
3
a = 10
a //= 3
print(a) # 输出:3
  1. 取模赋值 %=
    等价于 a = a % 右边的值
1
2
3
a = 10
a %= 3
print(a) # 输出:1
  1. 幂赋值 **=
    等价于 a = a ** 右边的值
1
2
3
a = 2
a **= 3
print(a) # 输出:8

3. 位运算符


位运算符(针对整数二进制),Python 里有 6 个位运算符,我们逐个来看:

运算符 名称 示例 结果 说明
& 按位与 3 & 1 1 对应位都为 1 则为 1
` ` 按位或 `3 1` 3 对应位有 1 则为 1
^ 按位异或 3 ^ 1 2 对应位不同则为 1
~ 按位取反 ~3 -4 二进制取反
<< 左移 3 << 1 6 二进制位左移,等价乘 2
>> 右移 3 >> 1 1 二进制位右移,等价除 2 取整
  • 前置说明:
    位运算符会把整数当成二进制数,对每一位(0 或 1)单独做运算,我们先看一个简单的二进制对应:
  • 十进制的 5 → 二进制的 101
  • 十进制的 3 → 二进制的 011
    Python 里可以用bin()函数把数字转成二进制字符串,方便你查看:
1
2
print(bin(5))  # 输出:0b101,0b开头代表二进制
print(bin(3)) # 输出:0b11
  1. 按位与 &
    两个数字的对应位都为 1,结果位才是 1,否则是 0
1
2
3
4
# 5: 101
# 3: 011
# &: 001 → 十进制的1
print(5 & 3) # 输出:1
  1. 按位或 |
    两个数字的对应位有一个为 1,结果位就是 1,只有都为 0 才是 0
1
2
3
4
# 5: 101
# 3: 011
# |: 111 → 十进制的7
print(5 | 3) # 输出:7
  1. 按位异或 ^
    两个数字的对应位不同,结果位就是 1,相同就是 0
1
2
3
4
# 5: 101
# 3: 011
# ^: 110 → 十进制的6
print(5 ^ 3) # 输出:6
  1. 按位取反 ~
    把所有位都反过来,1 变 0,0 变 1,注意 Python 的整数是补码存储,所以结果是 ~x = -x -1
1
2
print(~5)  # 输出:-6,不是-5!
print(~0) # 输出:-1
  1. 左移 <<
    把二进制位整体向左移动 n 位,右边补 0,等价于乘以 2 的 n 次方,速度非常快
1
2
3
4
5
6
# 5: 101
# 左移1位:1010 → 10,也就是5*2
print(5 << 1) # 输出:10

# 左移2位:10100 → 20,也就是5*4
print(5 << 2) # 输出:20
  1. 右移 >>
    把二进制位整体向右移动 n 位,左边补符号位,等价于除以 2 的 n 次方,向下取整
1
2
3
4
5
6
# 5: 101
# 右移1位:10 → 2,也就是5//2
print(5 >> 1) # 输出:2

# 负数的右移,向下取整
print(-5 >> 1) # 输出:-3,不是-2

==实用场景示例==

  • 位运算的速度非常快,很多底层、高性能场景会用到:
  1. 快速判断数字的奇偶
    利用奇数的二进制最后一位是 1,偶数是 0 的特点:
1
2
3
4
5
def is_odd(x):
return (x & 1) == 1

print(is_odd(5)) # 输出:True,奇数
print(is_odd(4)) # 输出:False,偶数
  1. 快速乘除 2
    左移右移比普通的乘除快很多(现在编译器会自动优化,但底层开发还是常用):
1
2
3
x = 10
print(x << 1) # 等价于x*2,输出20
print(x >> 1) # 等价于x//2,输出5
  1. 标志位:用一个变量存多个开关状态
    很多时候我们会用一个整数,存多个开关的状态,比如权限的读、写、执行,非常省空间:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 定义每个权限的标志,每个都是不同的二进制位
READ = 1 # 0b001
WRITE = 2 # 0b010
EXECUTE = 4 # 0b100

# 用户的权限,初始只有读
perm = READ
print(bin(perm)) # 0b1

# 添加写权限:用 | 把对应位加上
perm |= WRITE
print(bin(perm)) # 0b11,现在同时有读和写权限了

# 判断有没有执行权限:用 & 判断对应位是不是 1
if perm & EXECUTE:
print("有执行权限")
else:
print("没有执行权限")

# 删除写权限:用 &~ 把对应位去掉
perm &= ~WRITE
print(bin(perm)) # 0b1,只剩读权限了

Linux 的文件权限、很多底层的状态标志都是这么实现的。

  1. 交换两个变量(老技巧)
    不用临时变量,用异或交换两个整数,不过现在很少用了,了解即可:
1
2
3
4
5
6
a = 5
b = 3
a ^= b
b ^= a
a ^= b
print(a, b) # 输出:3 5,交换成功

⚠️ 注意:不要搞混位运算符和逻辑运算符
这是初学者最容易犯的错,&and|or完全不一样:

1
2
print(5 and 3)  # 逻辑与,输出:3
print(5 & 3) # 按位与,输出:1

⚠️ 注意:按位取反的结果不是你想的那样
不要以为~5-5,Python 的整数是补码存储,所以~x = -x -1

1
print(~5)  # 输出:-6,不是-5

⚠️ 注意:注意运算符的优先级
位运算的优先级比算术运算符低,而且内部也有优先级:<< >> > & > ^ > |,不确定就加括号:

1
2
3
4
5
# 等价于 (1+2) << 3,不是1 + (2<<3)
print(1 + 2 << 3) # 输出:24,不是25

# 不确定就加括号,避免歧义
perm = (perm & ~WRITE) | READ

⚠️ 注意:Python 的位运算不会溢出
C/Java 不同,Python 的整数是任意长度的,所以位运算永远不会溢出,多大的数都能正常运算:

1
2
# 很大的数做位运算也没问题
print(10**100 << 10) # 正常输出结果,不会溢出

4. 成员运算符📚


成员运算符用来判断某个元素,是不是属于某个容器(比如列表、字符串、元组、集合、字典这类可迭代对象),Python 里有两个:innot in

运算符 名称 示例 结果 说明
in 存在 "a" in "abc" True 判断元素是否在序列中
not in 不存在 "d" not in "abc" True 判断元素是否不在序列中
  1. 属于 in
    如果左边的元素,在右边的容器里,结果就是True,否则是False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 字符串:判断字符/子串是否存在
print('a' in 'abc') # 输出:True,字符a在字符串里
print('ab' in 'abc') # 输出:True,子串ab也能判断
print('d' in 'abc') # 输出:False

# 2. 列表:判断元素是否存在
print(3 in [1,2,3,4]) # 输出:True
print(5 in [1,2,3,4]) # 输出:False

# 3. 元组:和列表用法一致
print(3 in (1,2,3,4)) # 输出:True

# 4. 集合:判断元素是否存在
print(3 in {1,2,3,4}) # 输出:True

# 5. 字典:注意!判断的是**键**,不是值!
user = {'name': '小明', 'age': 18}
print('name' in user) # 输出:True,name是键
print('小明' in user) # 输出:False,小明是值,不是键!
print(18 in user) # 输出:False
  1. 不属于 not in
    in反过来,如果左边的元素,不在右边的容器里,结果就是True,否则是False
1
2
3
4
5
6
# 字符串
print('d' not in 'abc') # 输出:True
# 列表
print(5 not in [1,2,3,4]) # 输出:True
# 字典
print('gender' not in user) # 输出:True,user 里没有gender这个键

==实用场景示例==
成员运算符是日常开发里最常用的运算符之一:

  1. 验证用户输入的选项是否合法
    比如,判断用户输入的命令是不是我们支持的:
1
2
3
4
5
6
7
8
# 支持的命令
valid_cmds = ['start', 'stop', 'restart']
input_cmd = input("请输入命令:")

if input_cmd in valid_cmds:
print(f"执行命令:{input_cmd}")
else:
print(f"不支持的命令,支持的命令有:{valid_cmds}")
  1. 判断字符串里有没有特殊内容
    比如,判断用户的密码里有没有 @ 符号,或者文件名里有没有危险后缀:
1
2
3
4
filename = "test.exe"
# 判断文件名是不是exe后缀
if '.exe' in filename:
print("危险的可执行文件!")
  1. 过滤列表的元素
    比如,过滤出白名单里的元素:
1
2
3
4
5
6
7
8
# 白名单的用户 ID
white_ids = [1,2,3,4,5]
# 所有的用户 ID
all_ids = [1,3,6,7,2,9]

# 过滤出在白名单里的 ID
valid_ids = [x for x in all_ids if x in white_ids]
print(valid_ids) # 输出:[1, 3, 2]
  1. 安全判断字典的键
    避免访问不存在的键的时候,抛出 KeyError:
1
2
3
4
5
6
user = {'name': '小明'}
# 先判断 age 键存在不存在,再访问
if 'age' in user:
print(f"年龄:{user['age']}")
else:
print("用户没填年龄")

⚠️ 注意:字典的in是判断键,不是值
这是初学者最容易犯的错!字典的成员判断,永远是判断键,不是值,如果你要判断值在不在,要用in user.values()

1
2
3
user = {'name': '小明', 'age': 18}
# 想判断值'小明'在不在?要这么写
print('小明' in user.values()) # 输出:True

⚠️ 注意:大容器判断成员,优先用集合
列表、元组的in是逐个遍历,速度是 O (n),元素越多越慢;而集合的in是哈希查找,速度是 O (1),不管多大都很快:

1
2
3
4
5
6
7
8
# 如果你有一个很大的列表,要频繁判断成员,先转成集合
big_list = list(range(1000000))
# 慢:列表的 in 要遍历所有元素
print(999999 in big_list)

# 快:转成集合,一次查找就够了
big_set = set(big_list)
print(999999 in big_set)

⚠️ 注意:不要用in判断浮点数
因为浮点数的精度问题,很容易出错:

1
2
3
# 看起来应该是True?实际是False
print(0.1 + 0.2 in [0.3]) # 输出:False
# 因为 0.1 + 0.2 是 0.30000000000000004,不是 0.3

⚠️ 注意:字符串的in是判断子串
字符串的in不是只能判断单个字符,只要是连续的子串都能判断:

1
print('abc' in 'abcdef')  # 输出:True,整个子串都能匹配

5. 身份运算符📚


身份运算符用来判断两个变量,是不是指向内存里的同一个对象,你可以用id()函数,查看一个对象的内存地址,is的本质就是判断两个对象的id是不是一样。
它和我们之前学的 == 完全不一样:

  • ==:比较两个对象的内容是不是相等
  • is:比较两个对象的身份(内存地址)是不是同一个
运算符 名称 示例 结果 说明
is 是同一对象 a is b - 判断两个变量是否引用同一对象
is not 不是同一对象 a is not b - 判断两个变量是否引用不同对象
  1. 是同一个对象 is
    如果两个变量指向同一个对象,结果就是True,否则是False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 同一个对象的情况
a = [1,2,3]
b = a # b和a指向同一个列表
print(a is b) # 输出:True,同一个对象
print(id(a)) # 比如输出:140256189234560
print(id(b)) # 和上面的id完全一样,所以是同一个对象

# 2. 内容相同,但是不同对象的情况
a = [1,2,3]
b = [1,2,3]
print(a == b) # 输出:True,两个列表的内容完全一样
print(a is b) # 输出:False,两个是独立的不同对象,内存地址不一样
print(id(a)) # 140256189234624
print(id(b)) # 140256189234816,id不一样,所以不是同一个对象
  1. 不是同一个对象 is not
    is反过来,如果两个变量不是指向同一个对象,结果就是True
1
2
3
a = [1,2,3]
b = [1,2,3]
print(a is not b) # 输出:True,两个是不同的对象

📌Python 的小整数池特性
Python 为了优化性能,会把-5 ~ 256之间的小整数,提前缓存起来,整个程序里共用同一个对象,所以会出现这种情况:

1
2
3
4
5
6
7
a = 5
b = 5
print(a is b) # 输出:True,5是小整数,用的是同一个缓存对象

a = 257
b = 257
print(a is b) # 输出:False,257超出了小整数池,是两个不同的对象

⚠️ 注意:这个是 Python 的优化手段,不是语法规则,不要依赖这个来写代码!

==实用场景示例==
身份运算符最常用的场景,就是判断变量是不是None

1
2
3
4
5
6
7
8
# None是Python里的单例,整个程序里只有一个None对象
user = None

# 正确的写法:用is None判断
if user is None:
print("用户为空")
else:
print(f"用户:{user['name']}")

除此之外,有时候我们也会用它来判断两个变量是不是引用了同一个可变对象,避免修改的时候互相影响:

1
2
3
4
a = [1,2,3]
b = a
if a is b:
print("两个变量指向同一个列表,修改a会同步影响b")

⚠️ 永远不要搞混 is 和 ==
这是初学者最容易犯的错:

  • == 用来判断两个值的内容是不是相等,比如两个列表的内容是不是一样
  • is 用来判断两个变量是不是同一个对象,比如判断是不是 None
1
2
3
4
5
# 内容相同,但是不同对象,is会返回False
a = [1,2,3]
b = [1,2,3]
print(a == b) # True,内容一样
print(a is b) # False,不是同一个对象

⚠️ 不要用 is 判断整数、字符串的相等
不要因为小整数池的巧合,就用is来判断整数相等,比如:

1
2
3
4
5
6
7
# 巧合的True
print(5 is 5) # True,因为小整数缓存
# 但大的就不对了
print(257 is 257) # False,不要用is判断值相等!

# 正确的永远是用==
print(257 == 257) # True

字符串也是一样,短的字符串会被缓存,长的不会,所以也不要用 is 判断字符串相等。

⚠️ 判断 None 必须用 is None
永远不要用== None来判断,因为:

  1. is None的速度比== None快很多
  2. ==可以被自定义类重载,有些类会重载==,导致x == None返回 True,但 x 其实不是 None
  3. None 是单例,用is判断永远是准确的
1
2
3
4
5
6
7
# 正确
if x is None:
pass

# 错误,不要这么写
if x == None:
pass

⚠️ 不要依赖小整数池 / 字符串驻留
小整数池、字符串驻留都是 Python 的优化手段,不是语法规则,不同的 Python 版本、不同的运行环境(交互模式 / 脚本),结果可能不一样,所以永远不要依赖这个来写代码。

6. 比较运算符


运算符 名称 示例 结果 说明
== 等于 3 == 3 True 判断值是否相等
!= 不等于 3 != 4 True 判断值是否不等
> 大于 5 > 3 True 判断前者是否大于后者
< 小于 2 < 5 True 判断前者是否小于后者
>= 大于等于 5 >= 5 True 判断前者是否大于或等于后者
<= 小于等于 3 <= 5 True 判断前者是否小于或等于后者
比较运算符用来判断两个值的大小 / 相等关系,运算结果永远是布尔值:True(真)或 False(假)
  1. 等于 ==
    判断两个值的内容是否相等
1
2
3
print(5 == 5)       # 输出:True,两个值相等,结果为真
print(5 == 3) # 输出:False,两个值不相等,结果为假
print("abc" == "abc") # 输出:True,字符串也可以比较内容
  1. 不等于 !=
    判断两个值的内容是否不相等
1
2
print(5 != 3)       # 输出:True,两个值不相等
print(5 != 5) # 输出:False,两个值相等
  1. 大于 >
    判断左边的值是否大于右边
1
2
print(10 > 5)       # 输出:True
print(3 > 8) # 输出:False
  1. 小于 <
    判断左边的值是否小于右边
1
2
print(3 < 8)        # 输出:True
print(10 < 5) # 输出:False
  1. 大于等于 >=
    判断左边的值是否大于或等于右边python
1
2
print(10 >= 10)     # 输出:True
print(8 >= 10) # 输出:False
  1. 小于等于 <=
    判断左边的值是否小于或等于右边
1
2
print(5 <= 5)       # 输出:True
print(8 <= 5) # 输出:False

==Python 专属特性:链式比较==
Python 支持直接连写多个比较运算符,不用像其他语言那样用and拼接,写起来非常简洁:

1
2
3
4
5
6
7
8
# 想判断x是不是在1到10之间?
x = 5
# 普通写法(其他语言也能用)
is_in_range = x > 1 and x < 10
# Python专属链式写法,效果完全一样,更简洁!
is_in_range = 1 < x < 10

print(is_in_range) # 输出:True

这个特性在判断年龄、分数这类区间判断的时候特别好用。

7. 逻辑运算符


运算符 名称 示例 结果 说明
and 逻辑与 True and False False 全真为真,一假则假
or 逻辑或 True or False True 一真则真,全假为假
not 逻辑非 not True False 取反
  • 逻辑运算符用来做布尔逻辑判断,Python 里有三个:and(与)、or(或)、not(非)
  • 首先要知道:Python 里,以下值会被当做假(False)FalseNone00.0、空字符串''、空列表[]、空字典{},除此之外的所有值都是真(True)
  1. and
    只有左右两个条件都为真,最终结果才为真,只要有一个假,结果就是假
1
2
3
print(True and True) # 输出:True,两个都真 
print(True and False) # 输出:False,右边是假
print(5 > 3 and 2 > 1) # 输出:True,两个比较都为真
  1. or
    只要左右两个条件有一个为真,最终结果就为真,只有两个都假,结果才是假
1
2
3
print(True or False) # 输出:True,左边是真 
print(False or False) # 输出:False,两个都假
print(5 < 3 or 2 > 1) # 输出:True,右边的条件是真
  1. not
    把结果反过来,真变假,假变真
1
2
3
print(not True) # 输出:False 
print(not False) # 输出:True
print(not 5 > 3) # 输出:False,5>3是真,取反后是假

以下是 Python 的特殊设计,很多其他语言没有:

  • ==短路求值==
    • 逻辑运算符会 “偷懒”!如果左边的结果已经能决定最终结果,右边的代码就不会执行了:
1
2
3
4
5
6
7
8
9
10
11
12
13
# and的短路:左边已经是假了,右边直接跳过
a = 0
b = 5
if a > 0 and b > 0:
print("都大于0")
# 这里a>0已经是False了,右边的b>0根本不会判断,直接整个结果是False

# or的短路:左边已经是真了,右边直接跳过
a = 10
b = 5
if a > 0 or b > 0:
print("至少一个大于0")
# 这里a>0已经是True了,右边的b>0根本不会判断

这个特性很实用,比如我们经常用来做安全判断:if user is not None and user.age > 18,避免 user 是 None 的时候,访问 user.age 报错。

  • ==返回操作数,不是只有布尔值==
    • Python 的逻辑运算符,返回的不是只有True/False,而是返回最后一个参与运算的操作数
1
2
3
4
5
6
7
8
9
# and的规则:如果所有都为真,返回最后一个;如果有假,返回第一个假
print(5 and 3) # 输出:3,两个都为真,返回最后一个
print(0 and 5) # 输出:0,第一个是假,直接返回它
print("" and "abc") # 输出:'',空字符串是假,返回它

# or的规则:如果有真,返回第一个真;如果都假,返回最后一个
print(0 or 5) # 输出:5,第一个是假,看第二个,是真,返回它
print(3 or 0) # 输出:3,第一个是真,直接返回它
print("" or None) # 输出:None,两个都是假,返回最后一个
  • ==实用场景示例==
    • 展示逻辑运算符在实际代码中的用法:
  1. 判断多个条件同时满足
    比如,判断用户是不是成年,而且考试及格:
1
2
3
4
5
6
age = 18
score = 75
# 两个条件都要满足,用and
is_qualified = (age >= 18) and (score >= 60)
print(f"符合报名要求吗?{is_qualified}")
# 输出:符合报名要求吗?True
  1. 判断多个条件满足一个即可
    比如,判断用户是不是会员,或者积分够 100,就能免运费:
1
2
3
4
5
6
is_vip = False
user_points = 150
# 只要满足一个就行,用or
can_free_ship = is_vip or (user_points >= 100)
print(f"可以免运费吗?{can_free_ship}")
# 输出:可以免运费吗?True
  1. 给变量设置默认值
    利用or的特性,快速给空的变量设置默认值:
1
2
3
4
5
# 如果用户没输入名字,就默认用'匿名用户'
input_name = input("请输入你的名字:")
# 如果input_name是空的(假),就用后面的默认值
name = input_name or "匿名用户"
print(f"你好,{name}!")
  1. 取反判断特殊情况
    比如,过滤掉未成年的用户:
1
2
3
4
age = 16
if not (age >= 18):
print("未成年用户,无法访问该内容")
# 输出:未成年用户,无法访问该内容

⚠️ 注意:不要搞混逻辑运算符和位运算符
很多初学者会把and&or|搞混,这两个完全不一样:

  • and/or是逻辑判断,会短路,返回操作数
  • &/|是按位运算,不会短路,是对二进制位做运算
1
2
print(True and False) # 输出:False,逻辑与
print(True & False) # 输出:0,位运算的结果

⚠️ 注意:运算符的优先级
逻辑运算符的优先级是:not > and > or,很容易搞错,不确定的话加括号:

1
2
3
4
5
# 等价于 (not True) and False,不是 not (True and False)
print(not True and False) # 输出:False

# 不确定优先级就加括号,避免歧义
is_ok = (age >= 18) and (score >= 60)

⚠️ 注意:不要乱用and/or代替三元表达式
有些老代码会用b and a or c来代替三元表达式a if b else c,但如果 a 是假值(比如 0、空字符串),就会出错:

1
2
3
4
5
6
7
8
# 错误的写法!a是0,结果会变成c,不对
a = 0
b = True
c = 10
print(b and a or c) # 输出:10,本来应该是0

# 正确的三元表达式写法
print(a if b else c) # 输出:0,结果正确

⚠️ 注意:短路的副作用
如果右边的代码有修改变量、打印这类副作用,要注意短路会导致它不执行:

1
2
3
4
5
6
a = 0
b = 5
# 左边 a > 0 是 False,右边的 b = 10 根本不会执行!
if a > 0 and (b := 10) > 0:
pass
print(b) # 输出:5,不是10

8. 运算符的优先级


当一行代码里有多个运算符的时候,Python 会按照优先级的顺序,决定先算哪个、后算哪个,就像我们数学里的 ”先乘除后加减“ 。
把之前讲过的所有运算符,按照优先级从高到低排好了,你可以参考:

优先级(从高到低) 运算符 说明
1 () 括号,优先级最高,括号里的永远先算
2 ** 幂运算,注意是右结合
3 ~ 按位取反
4 * / // % 乘法、除法、整除、取模
5 + - 加法、减法
6 << >> 左移、右移
7 & 按位与
8 ^ 按位异或
9 | 按位或
10 <= < > >= 比较运算符
11 == != 等于、不等于
12 is is not 身份运算符
13 in not in 成员运算符
14 not 逻辑非
15 and 逻辑与
16 or 逻辑或
  1. 先乘除后加减
1
2
3
4
5
# 先算2*3=6,再算1+6=7
print(1 + 2 * 3) # 输出:7,不是9!

# 加括号可以改变顺序,先算1+2=3,再算3*3=9
print((1 + 2) * 3) # 输出:9
  1. 幂运算的优先级
1
2
3
4
5
# 先算3**2=9,再算2*9=18
print(2 * 3 ** 2) # 输出:18,不是36!

# 幂运算还是右结合的,先算右边的3**2=9,再算2**9=512
print(2 ** 3 ** 2) # 输出:512,不是64!
  1. 逻辑运算符的优先级
1
2
3
4
5
6
# 先算not True=False,再算False and False=False
print(not True and False) # 输出:False,不是True!

# 先算and,再算or,所以是True or False=True
print(True or False and False) # 输出:True,不是False!
# 等价于 True or (False and False),不是 (True or False) and False
  1. 比较运算符优先级比逻辑高
1
2
3
4
# 先算两个比较:3>2=True,2>1=True,再算and
print(3 > 2 and 2 > 1) # 输出:True
# 所以我们不用加括号,也能正常运行,这也是链式比较的基础
print(1 < 2 < 3) # 等价于 1<2 and 2<3,先算两个比较

📌如果记不住优先级,永远加括号
括号的优先级是最高的,不管什么运算符,加了括号,就会先算括号里的,永远不会错,而且代码的可读性也会更高。

比如:

1
2
3
# 不确定优先级?加括号
perm = (perm & ~WRITE) | READ
is_qualified = (age >= 18) and (score >= 60)

不用怕加括号,Python 不会嫌你加的多,反而能避免很多莫名其妙的 bug。

⚠️ 1. 不要搞错 and 和 or 的优先级
很多初学者以为 and 和 or 优先级一样,其实 and 比 or 高,所以:

1
2
3
# 你以为是 (True or False) and False = False?
# 实际是 True or (False and False) = True
print(True or False and False)

⚠️ 2. 不要搞错位运算和算术的优先级
算术的加减比位运算的 & 高,所以:

1
2
# 先算1+2=3,再算3&1=1
print(1 + 2 & 1) # 输出:1,不是1 + (2&1)=2

⚠️ 3. 不要搞错 not 的优先级
not 比 and 和 or 都高,所以:

1
2
# 先算not True=False,再算False and False
print(not True and False)

练习:


第一部分

  1. 以下表达式均为环境下运行,计算并写出最终结果:

    • 5 + 3 * 2
    • (5 + 3) * 2
    • 10 / 3
    • 10 // 3
    • 10.5 // 2
    • 10 % 3
    • 10.5 % 3
    • 2 ** 3
    • 2 ** -1
  2. 补全代码:输入圆的半径,计算圆的面积(公式:S=πr2,π 取 3.14)

1
2
3
4
r = float(input("请输入圆的半径:"))
pi = 3.14
area = ____ # 补全这行,用算术运算符计算面积
print("圆的面积是:", area)
  1. 补全代码:判断用户输入的整数是不是偶数(提示:用取模 % 运算符)
1
2
3
num = int(input("请输入一个整数:"))
is_even = ____ # 补全,判断是否能被2整除
print("是否为偶数:", is_even)
  1. 补全代码:输入一个总秒数,将其转换为「小时:分钟:秒」的格式输出。例如输入 3661,输出 1:1:1
1
2
3
4
5
6
total = int(input("请输入总秒数:"))
hour = ____ # 计算小时数
remain = total % 3600
minute = ____ # 计算剩余的分钟数
second = ____ # 计算剩余的秒数
print(f"{hour}:{minute}:{second}")
  1. 简单回答:Python3 中 /// 运算符的区别是什么?和 Python2 有什么不同?

  2. 以下代码能不能正常运行?如果不能,说明原因:

1
2
3
a = 5
a++
print(a)
  1. 计算:3 + 5 * 2 - 8 / 4 的结果是多少?

第二部分

  1. 思考:负数的取模和整除结果是什么?Python3 中计算并解释:

    • -7 % 3

    • -7 // 3

      为什么会得到这个结果?和部分其他语言的结果有区别吗?

  2. 浮点数精度问题:Python3 中运行 0.1 + 0.2,你会发现结果不是 0.3,而是 0.30000000000000004,思考这是为什么?这是 Python 的 bug 吗?

  3. 幂运算的结合性:Python3 中 2 ** 3 ** 2 的运行结果是多少?它等价于 (2**3)**2 还是 2**(3**2)?为什么?

  4. 数字拆分:如何用 //% 运算符,从三位数 123 中分别取出它的个位、十位、百位?写出代码实现。这个技巧在后续处理数字遍历的时候非常常用。

  5. 除零错误:Python3 中除数为 0 会抛出什么类型的错误?后续我们可以通过什么机制来捕获这类错误,避免程序崩溃?

  6. 大数运算:Python3 中计算 10 ** 1000 可以正常得到结果,不会溢出,思考这和 C、Java 甚至 Python2 的 long 类型有什么区别?Python3 的整数有大小限制吗?

参考答案


第一部分

  1. 表达式结果(Python3 环境):

    • 5 + 3 * 2 → 11(先乘后加,优先级规则)
    • (5 + 3) * 2 → 16(括号优先级最高)
    • 10 / 3 → 3.3333333333333335(普通除法,返回浮点数)
    • 10 // 3 → 3(整除,向下取整,整数操作返回整数)
    • 10.5 // 2 → 5.0(浮点数的整除,结果仍为浮点数)
    • 10 % 3 → 1(整数取模,取余数)
    • 10.5 % 3 → 1.5(浮点数也支持取模运算)
    • 2 ** 3 → 8(正指数幂,2 的 3 次方)
    • 2 ** -1 → 0.5(Python3 支持负指数幂,等价于 1/2)
  2. 补全面积计算:

1
area = pi * r ** 2
  1. 补全偶数判断:
1
is_even = num % 2 == 0
  1. 补全秒数转换:
1
2
3
4
hour = total // 3600
remain = total % 3600
minute = remain // 60
second = remain % 60
  1. /// 的区别(Python3 专属):

    • Python3 中,/普通除法,无论操作数是不是整数,都会返回浮点数结果,比如 10/3 得到 3.333...
    • //整除运算符,会对结果向下取整,操作数为整数时返回整数,浮点数返回浮点数。
    • 和 Python2 的区别:Python2 中,两个整数用 / 运算默认是整除,和 // 效果一样,Python3 修改了这个设计,让 / 永远返回浮点数,避免歧义。
  2. 代码不能运行。Python 不支持 ++ 自增运算符,这是 C / Java 等语言的语法,Python 中没有这个语法,要实现自增需要写 a += 1

  3. 表达式结果:3 + 10 - 2.0 = 11.0


第二部分

  1. 负数的运算结果:

    • -7 % 3 → 2

    • -7 // 3 → -3

      原因:Python 的整除是向下取整(向更小的数取整),-7/3 约等于 -2.333,向下取整就是 -3。同时 Python 保证 a = (a//b)*b + (a%b) 恒成立,所以 -7 = (-3)*3 + 2,因此余数是 2。

      部分语言(比如 C)的整除是向零取整,结果会不一样,这是 Python 的特殊之处。

  2. 浮点数精度问题:

    这不是 Python 的 bug,而是二进制浮点数的天生缺陷。计算机内部用二进制存储浮点数,0.10.2 都无法用二进制精确表示,存储的是近似值,相加后的近似值就会和 0.3 的近似值有微小误差。后续我们会学习 decimal 模块来解决高精度的小数运算问题。

  3. 幂运算的结合性:

    结果是 512,等价于 2**(3**2)

    因为幂运算在 Python 中是右结合的,大部分运算符是左结合(从左到右算),但幂运算反过来,从右到左计算,所以先算 3**2=9,再算 2**9=512

  4. 数字拆分:

1
2
3
4
5
num = 123
hundreds = num // 100 # 百位:123//100=1
tens = (num // 10) % 10 # 十位:12%10=2
units = num % 10 # 个位:123%10=3
print(hundreds, tens, units) # 输出 1 2 3
  1. 除零错误:

    除数为 0 会抛出 ZeroDivisionError 异常。后续我们可以通过 异常处理(try-except) 机制来捕获这类错误,让程序在遇到错误时不会直接崩溃,而是可以给出友好的提示。

  2. 大数运算:

    C / Java 等语言的整数有固定的位数限制(比如 int 是 32 位),超过就会溢出;Python2 的 long 也有隐式的长度限制;而 Python3 去掉了 long 类型,统一为任意精度整数,理论上没有大小限制,只要内存足够,多大的数都能存,所以不会有溢出问题。