第 3 章 变量、表达式与运算符
3.1 变量赋值与命名规则
Python 中变量不需要声明类型,赋值即创建:
name = "Alice" # 字符串
age = 30 # 整数
height = 1.68 # 浮点数
is_student = True # 布尔
# 多重赋值
x = y = z = 0
# 解包赋值
a, b, c = 1, 2, 3
# 交换变量 — Python 特有的优雅写法
a, b = b, a
命名规则
- 只能包含字母、数字、下划线,不能以数字开头
- 区分大小写:
name和Name是不同变量 - 不能使用关键字(
if、for、class等)
# 查看所有关键字
import keyword
print(keyword.kwlist)
# ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await',
# 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except',
# 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
# 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
# 'try', 'while', 'with', 'yield']
命名风格
# 推荐的命名风格(PEP 8)
user_name = "Bob" # 变量:snake_case
MAX_RETRIES = 3 # 常量:UPPER_SNAKE_CASE
class UserProfile: pass # 类名:PascalCase
def get_user(): pass # 函数:snake_case
_private_var = "secret" # 私有:前缀下划线
3.2 动态类型与变量引用模型
Python 变量是标签(引用),不是盒子。变量名绑定到对象,而非存储值。
a = [1, 2, 3]
b = a # b 和 a 指向同一个列表对象
b.append(4)
print(a) # [1, 2, 3, 4] — a 也变了!
# 验证:id() 返回对象的内存地址
print(id(a) == id(b)) # True — 同一个对象
图示理解:
a ──→ [1, 2, 3, 4] ←── b # a 和 b 是两个标签,贴在同一个对象上
重新赋值 vs 修改对象:
a = [1, 2, 3]
b = a
# 重新赋值 — b 指向新对象,a 不变
b = [4, 5, 6]
print(a) # [1, 2, 3]
# 修改对象 — a 和 b 指向同一对象,都受影响
a = [1, 2, 3]
b = a
b[0] = 99
print(a) # [99, 2, 3]
动态类型
变量可以随时绑定到不同类型的对象:
x = 42 # int
x = "hello" # str — 合法!类型跟着值走
x = [1, 2, 3] # list — 又变了
# type() 查看当前类型
print(type(x)) # <class 'list'>
可变与不可变对象
| 不可变(immutable) | 可变(mutable) |
|---|---|
| int, float, bool | list |
| str, tuple | dict |
| frozenset, bytes | set, bytearray |
# 不可变对象 — "修改"实际上是创建新对象
a = "hello"
print(id(a)) # 比如 140234567890
a = a + " world"
print(id(a)) # 不同了!新对象
# 可变对象 — 原地修改
lst = [1, 2, 3]
print(id(lst)) # 比如 140234567900
lst.append(4)
print(id(lst)) # 不变!同一个对象
小整数缓存
CPython 优化:-5 到 256 的整数会被缓存复用:
a = 256
b = 256
print(a is b) # True — 同一个对象(缓存)
a = 257
b = 257
print(a is b) # False — 不同对象(注意:在 REPL 中可能为 True)
3.3 算术运算符
# 基本运算
10 + 3 # 13 加
10 - 3 # 7 减
10 * 3 # 30 乘
10 / 3 # 3.333... 真除法(结果总是 float)
10 // 3 # 3 整除(向下取整)
10 % 3 # 1 取模
10 ** 3 # 1000 幂运算
# 整除的"向下取整"特性
-7 // 2 # -4(不是 -3!向负无穷方向取整)
7 // -2 # -4
# divmod — 同时获得商和余数
divmod(17, 5) # (3, 2)
# abs — 绝对值
abs(-42) # 42
# round — 四舍五入(银行家舍入法)
round(2.5) # 2(不是 3!四舍六入五取偶)
round(3.5) # 4
round(3.14159, 2) # 3.14
类型提升规则
# int + float → float
3 + 2.0 # 5.0
# int + complex → complex
3 + 2j # (3+2j)
# bool 参与运算时当作 int
True + 1 # 2
False * 10 # 0
3.4 比较运算符与链式比较
# 基本比较
3 == 3 # True
3 != 4 # True
3 < 5 # True
3 > 5 # False
3 <= 3 # True
3 >= 4 # False
# 链式比较 — Python 特色!
x = 5
1 < x < 10 # True,等价于 1 < x and x < 10
1 < x < 3 # False
1 <= x <= 5 <= 10 # True
== vs is
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True — 值相等
a is b # False — 不是同一个对象
a = b
a is b # True — 现在是同一个对象了
# 经验法则:
# == 比较值,is 比较身份(内存地址)
# 只有和 None 比较时才用 is
不同类型的比较
# 数字之间可以比较
42 == 42.0 # True
True == 1 # True
# 字符串按字典序比较
"apple" < "banana" # True
"abc" < "abd" # True
# 不同类型之间大多数情况不能比较
# "abc" < 123 # TypeError(Python 3)
3.5 逻辑运算符:and、or、not 及短路求值
True and False # False
True or False # True
not True # False
短路求值(Short-circuit Evaluation)
and 和 or 不一定返回 True/False,而是返回决定结果的那个值:
# and: 如果第一个为假,直接返回第一个;否则返回第二个
0 and "hello" # 0(第一个为假,短路)
"hello" and "world" # "world"(第一个为真,返回第二个)
"" and "world" # ""(空字符串为假)
# or: 如果第一个为真,直接返回第一个;否则返回第二个
0 or "hello" # "hello"(第一个为假,返回第二个)
"hello" or "world" # "hello"(第一个为真,短路)
"" or "default" # "default"
实用技巧:
# 提供默认值(被 := 海象运算符和 or 取代的场景)
name = user_input or "Anonymous"
# 条件执行
debug = True
debug and print("调试信息") # debug 为 True 时才打印
# 等价于
if debug:
print("调试信息")
布尔运算优先级
not > and > or:
# not > and > or
True or False and not False
# 等价于: True or (False and (not False))
# = True or (False and True)
# = True or False
# = True
3.6 位运算符
用于对整数的二进制位进行操作:
a = 0b1100 # 12
b = 0b1010 # 10
a & b # 0b1000 = 8 按位与
a | b # 0b1110 = 14 按位或
a ^ b # 0b0110 = 6 按位异或
~a # -13 按位取反(补码)
a << 2 # 0b110000 = 48 左移
a >> 2 # 0b0011 = 3 右移
# 格式化查看二进制
print(f"{a:08b}") # 00001100
print(f"{b:08b}") # 00001010
常见应用:
# 判断奇偶
n = 7
is_odd = n & 1 # 1 → 奇数
is_even = not (n & 1) # False → 不是偶数
# 标志位
READ = 0b001 # 1
WRITE = 0b010 # 2
EXECUTE = 0b100 # 4
permission = READ | WRITE # 0b011 = 3
has_read = permission & READ # 0b001 = 1 → True
has_exec = permission & EXECUTE # 0b000 = 0 → False
# 交换两个整数(不用临时变量,但可读性差,不推荐)
a = a ^ b
b = a ^ b
a = a ^ b
3.7 赋值运算符与海象运算符
增强赋值
x = 10
x += 3 # x = x + 3 → 13
x -= 2 # 11
x *= 4 # 44
x /= 11 # 4.0
x //= 3 # 1.0
x **= 3 # 1.0
x %= 7 # 1.0
# 对于可变对象,+= 是原地修改
lst = [1, 2]
original_id = id(lst)
lst += [3, 4] # 调用 __iadd__,原地扩展
print(id(lst) == original_id) # True
# 对于不可变对象,+= 创建新对象
s = "hello"
original_id = id(s)
s += " world"
print(id(s) == original_id) # False
海象运算符 :=(Python 3.8+)
在表达式内部赋值,避免重复计算:
# 不用海象运算符
line = input("请输入: ")
while line != "quit":
print(f"你输入了: {line}")
line = input("请输入: ")
# 用海象运算符 — 更简洁
while (line := input("请输入: ")) != "quit":
print(f"你输入了: {line}")
# 在条件判断中避免重复计算
data = [1, 5, 3, 8, 2, 9, 4, 7]
# 不用海象运算符
results = []
for x in data:
y = x ** 2 + x
if y > 20:
results.append(y)
# 用海象运算符
results = [y for x in data if (y := x ** 2 + x) > 20]
print(results) # [30, 72, 90, 56]
# 在正则匹配中
import re
text = "Phone: 138-1234-5678"
# 不用海象运算符
match = re.search(r"\d{3}-\d{4}-\d{4}", text)
if match:
print(f"找到号码: {match.group()}")
# 用海象运算符
if match := re.search(r"\d{3}-\d{4}-\d{4}", text):
print(f"找到号码: {match.group()}")
3.8 运算符优先级
从高到低排列(同一行优先级相同):
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 最高 | () | 括号 |
** | 幂运算 | |
+x, -x, ~x | 一元运算 | |
*, /, //, % | 乘除 | |
+, - | 加减 | |
<<, >> | 位移 | |
& | 按位与 | |
^ | 按位异或 | |
| | 按位或 | |
==, !=, <, >, <=, >=, is, in | 比较 | |
not | 逻辑非 | |
and | 逻辑与 | |
| 最低 | or | 逻辑或 |
实用建议:不要死记优先级,多加括号让代码更清晰:
# 不推荐:依赖优先级
result = a + b * c ** 2 > d and e or f
# 推荐:用括号明确意图
result = ((a + (b * (c ** 2))) > d) and e) or f
# 或者拆成多行
power = c ** 2
product = b * power
is_greater = (a + product) > d
result = (is_greater and e) or f
本章小结:Python 的变量是引用而非容器,理解这一点对避免可变对象的陷阱至关重要。运算符方面,链式比较、短路求值、海象运算符是 Python 的独特特性,善用它们能写出更简洁的代码。