日常写 Python,import、def、for 用得飞起,但一旦遇到边界情况——整数除法、变量作用域、异常捕获顺序——就容易翻车。这篇文章围绕变量、数据类型、运算符、表达式、关键字和异常这六大板块,把高频踩坑点逐一拆开,并附上可直接运行的验证脚本。
变量与赋值:不是"贴标签"那么简单
Python 变量是引用,不是容器。同一个对象可以被多个变量指向:
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4] — a 也变了
要避免意外共享,用拷贝:
b = a.copy() # 浅拷贝,够用就行
b = a[:] # 切片拷贝,等价写法
另一个经典陷阱——小整数缓存:-5 到 256 的整数在 CPython 中被预创建,x == y 且 x is y 都为 True;超出范围就未必了:
x = 300
y = 300
print(x == y) # True
print(x is y) # False(大部分场景)
面试或调试时,搞清 ==(值相等)和 is(身份相同)的区别,能省不少时间。
数据类型:隐式转换与边界行为
整数除法
Python 3 里 / 总是浮点结果,// 才是整除:
print(7 / 2) # 3.5
print(7 // 2) # 3
print(-7 // 2) # -4 — 向下取整,不是截断
负数整除向负无穷方向靠,这和 C/Java 的截断行为不同,写索引计算时要格外小心。
浮点精度
print(0.1 + 0.2 == 0.3) # False
print(0.1 + 0.2) # 0.30000000000000004
需要精确比较时,用 math.isclose 或 decimal.Decimal:
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2') == Decimal('0.3')) # True
布尔值是 int 的子类
True 就是 1,False 就是 0:
print(True + True) # 2
print(isinstance(True, int)) # True
所以 if x 里 x 为 0、""、[]、None 时都走 False 分支——记住完整的"假值表"比逐个猜更靠谱。
运算符与表达式:优先级与短路
优先级坑点
not 的优先级高于 and,高于 or:
not a and b or c
# 等价于 ((not a) and b) or c,而不是 not (a and (b or c))
拿不准就加括号,可读性也更好。
短路求值
and / or 会短路,且返回的是决定结果的那个值,不一定是 True/False:
print(3 or 0) # 3
print(0 or 3) # 3
print(3 and 0) # 0
print(0 and 3) # 0
利用这个特性可以写简洁的默认值逻辑:name = input_name or "anonymous"。
关键字:几个容易混淆的
| 关键字 | 常见误用 |
|---|---|
global |
在函数内声明后才能修改全局变量,只读不需要 |
nonlocal |
嵌套函数中修改外层局部变量,不是全局 |
yield |
让函数变成生成器,return 仍然可用但会终止生成 |
assert |
可被 python -O 关闭,不要用它做业务校验 |
一个 nonlocal 的正确场景:
def counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
c = counter()
print(c()) # 1
print(c()) # 2
如果漏写 nonlocal,n += 1 会报 UnboundLocalError——Python 看到赋值就把 n 当成局部变量,但赋值前又读了它,矛盾了。
异常处理:顺序、类型与 finally
捕获顺序从窄到宽
try:
int("abc")
except ValueError:
print("值错误")
except Exception: # 必须在更窄的异常之后
print("其他错误")
如果先写 except Exception,ValueError 永远不会被单独捕获。
else 和 finally 的语义
else:try块没有抛异常时执行,适合放"成功后的逻辑",避免意外捕获不属于try的异常。finally:无论如何都执行,常用于资源释放。
try:
f = open("data.txt")
data = f.read()
except FileNotFoundError:
print("文件不存在")
else:
print(f"读到 {len(data)} 字节")
finally:
f.close() # 注意:如果 open 就失败了,f 未定义,这里会报错
更安全的写法是用 with,让上下文管理器替你关文件。
实战验证脚本
把上面提到的坑点集中到一个脚本里,逐条运行、逐条确认:
#!/usr/bin/env python3
"""Python 基础踩坑验证脚本 — 直接运行即可"""
# 1. 变量共享 vs 拷贝
a = [1, 2, 3]
b = a
b.append(4)
print("1. 共享引用:", a) # 预期 [1, 2, 3, 4]
c = a.copy()
c.append(5)
print("1. 拷贝后互不影响:", a) # 预期 [1, 2, 3, 4],不含 5
# 2. 小整数缓存
x, y = 256, 256
print("2. 256 is:", x is y) # 预期 True
x, y = 257, 257
print("2. 257 is:", x is y) # 预期 False(多数环境)
# 3. 整除方向
print("3. -7//2 =", -7 // 2) # 预期 -4
# 4. 浮点精度
print("4. 0.1+0.2 =", 0.1 + 0.2) # 预期 0.30000000000000004
# 5. 布尔是 int 子类
print("5. True+True =", True + True) # 预期 2
# 6. 短路返回值
print("6. 3 or 0 =", 3 or 0) # 预期 3
print("6. 0 or 3 =", 0 or 3) # 预期 3
# 7. nonlocal
def counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
c = counter()
print("7. counter:", c(), c()) # 预期 1 2
# 8. 异常捕获顺序
try:
int("abc")
except ValueError:
print("8. 捕获 ValueError") # 预期走到这里
except Exception:
print("8. 捕获 Exception")
# 9. try-else-finally
try:
result = 10 / 2
except ZeroDivisionError:
print("9. 除零")
else:
print("9. 成功, result =", result) # 预期走到这里
finally:
print("9. finally 总会执行")
print("--- 全部验证完成 ---")
运行方式:
python3 python_fundamentals_check.py
如果某条输出和预期注释不一致,说明你的 Python 环境(版本或实现)有差异,正好值得深挖原因。
复盘清单
每次觉得自己"Python 基础没问题"时,可以用这张清单快速自测:
- 变量:赋值是引用还是拷贝?
is和==分别比较什么? - 类型:整除对负数怎么取?浮点比较用什么方法?哪些值在
if中为假? - 运算符:
not/and/or优先级顺序?短路返回的是布尔值还是原值? - 关键字:
globalvsnonlocal区别?assert能被关闭吗? - 异常:捕获顺序从窄到宽?
else和finally各在什么时候执行?
基础不是"入门后就不用再看"的知识,而是写代码时每天都在做的小决策。把这些细节吃透,调试时间会明显缩短,代码意图也更干净。