Python 3 基础速查
面向复习与日常开发的 Python 3 基础速查,涵盖核心语法、对象与类型、容器、函数、异常、模块和工程常识。
版本:示例使用 Python 3.13,按对应版本官方文档复核;实际项目以自身声明的 Python 版本为准。
1. Python、CPython 与交互环境
| 名称 | 定位 |
|---|---|
| Python | 语言及其规范 |
| CPython | 最常用、由 Python 项目维护的实现 |
| PyPy | 包含 JIT 的另一种实现,第三方扩展兼容性需要单独核对 |
| IPython、Jupyter | 交互环境,通常运行在 CPython 之上 |
源码不一定被直接逐行解释执行。 以默认 CPython 为例,源码先编译为字节码,再由虚拟机执行,并可能缓存 .pyc。
具体执行方式取决于实现。Python 通常以一定运行时开销换取表达力、生态和开发效率,性能是否够用应以实测为准。
2. 安装、运行与环境隔离
尚未安装时,从 python.org 下载页 获取 Python。
- 新项目:可以选择当前稳定版。
- 已有项目:以项目声明的 Python 版本和依赖兼容范围为准。
安装完成后,先确认终端实际调用的是哪个版本。
2.1 确认解释器
macOS 或 Linux 通常使用 python3,Windows 可使用 py 启动器:
| |
| |
直接运行解释器会进入 REPL,适合验证表达式;把代码保存为 hello.py 后则可以运行脚本:
| |
| |
| |
2.2 用 venv 快速建立项目环境
已经安装 Python 后,可以先用标准库 venv 完成最小环境隔离实践。
- 作用:为当前项目建立独立的第三方包目录,避免不同项目互相覆盖依赖。
- 前提:基于现有 Python 创建环境,不会替你安装另一个 Python 版本。
- 目录:
.venv只存放环境,不放项目源码,也不提交到 Git。
| |
| |
激活后统一使用当前环境中的 python。用 python -m pip 比直接使用 pip 更明确,因为它能保证安装目标就是当前解释器:
| |
不要用 sudo pip install ... 修改系统 Python。
项目依赖应记录在 pyproject.toml 或锁文件中,虚拟环境本身可随时重建。
使用完毕后,退出当前 shell 的虚拟环境:
| |
venv 与 Miniconda 怎么选?
venv:同样可用于生产项目,不能用“初学者 / 企业级”划分工具。- Miniconda:包含 conda、Python 及必要依赖的精简发行版。conda 可管理不同 Python 版本及非 Python 依赖。
选择取决于依赖和部署方式。Miniconda 的安装与环境管理另文介绍,本文先用 venv 跑通基础示例。
3. 语法、注释与命名
3.1 缩进是语法的一部分
Python 用缩进表示代码块。每层使用 4 个空格,不要混用 Tab 与空格。
为什么这样约定?
- 4 个空格是 PEP 8 风格建议,不是解释器强制的唯一宽度。
- 解析行首缩进时,Python 将 Tab 展开到下一个 8 列制表位,编辑器却可能按 4 列显示;视觉对齐不代表缩进层级相同。
- 混用导致缩进含义依赖 Tab 宽度时,会触发
TabError,它属于IndentationError。
编辑器建议:把 Tab 键设为插入 4 个空格。 按了 Tab 键,不等于文件里存了 Tab 字符。
| |
3.2 注释不等于文档字符串
- 注释:
#从当前位置注释到行尾;多行注释需要每行都加#。 - 跨行字符串:
'''...'''和"""..."""都可以使用,前后必须是同一种引号。三引号不是专用的“多行注释”语法。 - 文档字符串:普通字符串字面量放在模块、类或函数体的第一条语句,可通过
__doc__或help()读取;惯例用三个双引号。
单独放在其他位置的三引号字符串通常没有可见效果,但内部转义和引号仍受字符串语法约束,不能把任意代码包进去当注释。
| |
注释应该解释“为什么”,而不是逐字复述代码。过时注释比没有注释更危险。
3.3 命名规则与惯例
标识符区分大小写,可以包含 Unicode 字母、数字和下划线,但不能以数字开头,也不能使用关键字。关键字应动态查看:
| |
Python 3.13 输出:
| |
常用的 PEP 8 命名约定:
- 变量、函数、模块:
snake_case - 类:
CapWords - 常量:
UPPER_CASE - 内部实现细节:前导下划线,如
_cache
全大写只是“不要重新赋值”的协作约定,不会让对象真正不可变。
4. 名称、对象与类型
4.1 赋值是名称绑定
Python 变量更准确的说法是“名称绑定到对象”,而不是一个固定类型的内存盒子:
| |
- 动态类型:名称可以重新绑定到不同类型的对象。
- 强类型:通常不会偷偷把无关类型混合运算,例如整数与字符串不能直接相加。
| |
多个名称可以指向同一个可变对象:
| |
需要独立的浅拷贝可使用 first.copy() 或 first[:];嵌套对象是否也要复制,需要根据数据结构决定是否使用 copy.deepcopy()。
4.2 常见内置类型
| 类别 | 类型 | 是否可变 | 示例 |
|---|---|---|---|
| 空值 | NoneType | 否 | None |
| 布尔 | bool | 否 | True、False |
| 数值 | int、float、complex | 否 | 42、3.14、1 + 2j |
| 文本与字节 | str、bytes | 否 | "你好"、b"data" |
| 序列 | list、tuple、range | 仅 list 可变 | [1, 2]、(1, 2) |
| 映射 | dict | 是 | {"name": "Ada"} |
| 集合 | set、frozenset | 仅 set 可变 | {1, 2} |
“对象不可变”表示对象自身的值不能原地改变,不代表指向它的名称不能重新绑定。
complex:复数
- 组成:
a + bj中,a是实部,b是虚部系数,j表示平方为-1的虚数单位。 - 写法:Python 写
1j;单独的j会被当成变量名。 - 用途:支持加减乘除,常见于科学计算和信号处理;普通业务先认识表示法即可。
| |
set 与 frozenset:可变集合与不可变集合
二者都保存不重复的可哈希元素,支持交集、并集等运算。
| 类型 | 能否原地增删 | 自身能否作为字典键或集合元素 |
|---|---|---|
set | 可以 | 不可以,自身不可哈希 |
frozenset | 不可以 | 可以,自身可哈希 |
集合运算可以产生新对象,不等于修改原对象。
| |
4.3 真值规则
None、False、数值零以及空字符串和空容器通常为假,其余对象通常为真:
| |
if value: 同时把 0、空值和空容器视为假。如果业务上必须区分这些情况,就应写出精确条件。
5. 数值与运算符
常用算术运算符包括 +、-、*、/、//、% 和 **。对内置 int、float,/ 执行除法并返回 float,
// 则把商向负无穷取整:
| |
int() 把浮点数向零截断,不等于数学上的向下取整:
| |
浮点数比较:math.isclose()
浮点数采用二进制表示,很多十进制小数不能精确存储。
math.isclose(a, b) 判断两数是否在允许误差内接近,返回布尔值。
rel_tol:相对误差。以两数绝对值中较大的一个为基准,默认1e-9。abs_tol:绝对误差。默认0.0;比较接近零的数时,通常要指定正的abs_tol。
误差阈值由业务确定。这个函数不会修改数值,也不等于先保留几位小数再比较。
| |
十进制计算:Decimal
Decimal 可精确表示 0.1 这类有限十进制小数,适合金额等需要明确十进制精度的计算。
- 从字符串构造,如
Decimal("0.1"),避免带入float已有的二进制误差。 - 运算仍有精度限制,例如不能把除法结果理解成无限精确。
- 明确舍入规则,金额保留几位、如何取舍都应由业务规定。
| |
比较运算可以链式书写,逻辑运算使用 and、or、not:
| |
6. 字符串、字节与格式化输出
6.1 字符串是不可变的 Unicode 文本
| |
切片遵循左闭右开区间 [start:stop:step]。字符串方法返回新对象,不会原地修改原字符串。
6.2 优先使用 f-string
% 格式化和 str.format() 仍然有效,但新代码通常优先使用可读性更好的 f-string:
| |
转义字符 \n 表示换行,\t 表示水平制表符;制表符显示成多宽由终端或编辑器决定。
| |
输出:
| |
6.3 文本与字节要显式转换
网络、压缩和二进制文件通常处理 bytes;界面和业务文本通常处理 str。二者不能直接拼接:
| |
- 编码:
str → bytes,使用.encode()。 - 解码:
bytes → str,使用.decode(),按对应编码解释字节。
Python 3 的 str 已经是文本,没有 .decode() 方法,不能再次解码。
字节可以来自 .encode(),也可以直接从文件或网络读取,不要求先在当前程序里编码一次。
读写文本文件时应明确指定编码。
7. 输入与安全转换
input() 总是返回字符串。转换可能失败,应处理具体的 ValueError:
| |
两种输入的执行结果:
| |
读取密码:getpass.getpass()
getpass.getpass() 是标准库中的密码输入函数,返回 str。
- 与
input()的区别:在支持的终端中不回显键入字符,通常连星号也不显示。 - 职责边界:只负责输入,不负责加密或验证密码;读取后也不要打印密码。
| |
部分 IDE 控制台不支持隐藏输入,可能出现 GetPassWarning 并回显;遇到这种情况改在终端运行。
选择安全的转换方式
不要把 eval() 当成类型转换工具。 它会执行 Python 表达式,对不可信输入使用可能导致任意代码执行。
- 已知目标类型:使用
int()、float()、str()。 - JSON 数据:使用
json.loads()。它按 JSON 语法解析数据,不执行 Python 表达式。 - 语法区别:JSON 用
true、null,Python 用True、None。
两者的详细对比留到基础易混淆点文章。
| |
8. 核心容器
8.1 列表与元组
列表适合可变的有序数据,元组适合结构固定的记录或不可变序列:
| |
元组本身不能增删或替换元素,但元素仍可能指向列表等可变对象,因此元组不保证“深层不可变”,也不一定可哈希。
避免用乘法创建共享的嵌套可变对象:
| |
8.2 字典
字典保存键值映射,键必须可哈希。字典保留插入顺序,但业务需要排序时,仍应显式使用 sorted()。
为什么列表不能作为键?
- 定位方式:字典借助哈希值定位键。可哈希对象的哈希值在生命周期中保持不变,相等对象具有相同哈希值。
- 列表不可哈希:列表内容可变,Python 不给列表提供哈希能力,因此
{[1, 2, 3]: "abc"}会报错。 - 可用的键:整数、字符串,以及元素也全部可哈希的元组。
| |
不可变不等于一定可哈希。 元组里含列表时仍不可哈希;自定义对象取决于它的哈希与相等性实现。
| |
输出:
| |
读取键:下标还是 get()?
- 必填字段用
mapping[key]:缺失时抛出KeyError。例如姓名必填,user["name"]能及时暴露缺字段问题。 - 可选字段用
mapping.get(key):缺失时默认返回None。例如用户可以不填邮箱,user.get("email")就适合。
注意拼写:把 name 错写成 user.get("nmae"),也只会得到 None,错误可能拖到后续步骤才出现。
8.3 集合
集合保存不重复、可哈希的元素,适合去重和成员判断;集合本身没有可依赖的展示顺序。
| |
8.4 推导式
推导式适合简短、单一的映射或过滤;逻辑复杂时使用普通循环更清楚。
| |
生成器表达式使用圆括号,按需产生值,不会一次构造完整列表。
下面示例中的数字写法:
1_000_000就是1000000:数字下划线是 Python 3.6 起支持的标准分隔写法,只提高可读性。range(1_000_000):依次产生0到999999。
| |
9. 分支、循环与模式匹配
9.1 if、for 与 while
Python 的 for 遍历可迭代对象,而不是只提供计数循环:
| |
输出:
| |
并行遍历多个序列可使用 zip():
| |
输出:
| |
strict=True 会在迭代发现序列长度不一致时抛出 ValueError,适合长度必须相同的业务数据。
while 适合结束条件不是“遍历完某个集合”的情况。循环必须保证状态朝退出条件推进:
| |
输出:
| |
9.2 match 是结构化模式匹配
match 是 Python 3.10 新增的模式匹配语句:按顺序比较各个 case 后的模式,只执行第一个匹配分支。
case 是软关键字,不是函数。 它既能匹配固定值,也能检查序列结构并取出元素。
下面把命令表示为元组。模式 ("move", direction, distance) 分三部分理解:
- 结构:序列必须有三个元素。
- 固定值:首元素必须为
"move"。 - 捕获变量:后两个元素分别绑定给
direction、distance,不是与同名旧变量比较。
| |
使用时记住:
- 无需
break:匹配后不会自动继续执行下一个case。 - 没有匹配就跳过:没有分支匹配且没有
case _时,整个语句直接结束。 - 序列模式也能匹配列表:参数类型标注本身不会限制运行时传入的类型。
简单条件继续用 if 即可。
10. 函数与作用域
10.1 参数与返回值
函数可以组合位置参数、默认参数和仅限关键字参数:
| |
*后的参数:必须按关键字传递,避免布尔值、数字等位置参数含义不清。*args:收集额外位置参数。**kwargs:收集额外关键字参数。
但普通业务函数不应无理由接受任意参数。
函数没有显式 return 时返回 None。返回多个值实际上是返回一个元组:
| |
10.2 不要使用可变默认值
默认参数在函数定义时求值,只创建一次:
| |
这个规则同样适用于字典、集合以及自定义的可变对象。
10.3 作用域与闭包
名称查找通常遵循 LEGB:局部(Local)→ 外层函数(Enclosing)→ 全局(Global)→ 内置(Built-in)。
尽量通过参数和返回值传递数据,少依赖可变全局状态。
| |
lambda 适合短小的单表达式函数,例如排序键;一旦需要分支、异常处理或文档,就应使用 def。
| |
11. 异常与资源管理
异常用于报告当前层不能正常完成的操作。捕获异常时应尽可能具体,不要用裸 except: 隐藏退出、取消和程序错误。
| |
异常结构的职责:
try:只包围可能失败的最小范围。except:处理明确知道如何恢复或转换的异常。else:仅在没有异常时执行。finally:无论是否异常都执行,适合必须进行的清理。
文件和锁等资源优先使用 with,由上下文管理器保证释放,通常无需手写 finally。
假设 notes.txt 包含“第一行”和“第二行”:
| |
输出:
| |
数据库连接:核对 with 的具体职责
sqlite3.Connection的with只管理事务提交或回滚,不会关闭连接。- 关闭连接仍需显式调用
close(),或另外使用能负责关闭的上下文管理器。
文件操作:区分状态检查与实际操作
- 查看状态:
Path.exists()可判断此刻是否存在,但不能替代操作本身的错误处理。 - 读写文件:使用明确的打开模式,捕获具体异常;不要用“读取失败就新建文件”的宽泛异常逻辑判断是否存在。
- 保证约束:“先检查再操作”可能有并发竞态,真正的写入约束应由打开模式或底层存储保证。
12. 文件、路径与结构化数据
12.1 使用 pathlib 表达路径
pathlib.Path 把路径表示为对象,比手工拼接 / 或 \\ 更跨平台。
Path("data") 表示什么?
- 位置:相对于当前工作目录的
data路径。 - 创建对象:不会创建目录,也不会自动变成绝对路径;调用
mkdir()才会创建目录。 - 系统差异:macOS / Linux 中是
PosixPath,Windows 中是WindowsPath。
为什么会看到 PosixPath('data')?
| 查看方式(macOS / Linux) | 显示内容 |
|---|---|
交互环境直接查看对象,或使用 repr() | PosixPath('data'),包含对象类型 |
使用 print() | data,只显示路径文本 |
下面假设在 macOS 的 /Users/you/project 目录启动程序,且路径不涉及符号链接:
| |
路径对象之间的 / 表示拼接子路径。
下面创建目录并写入文件。data_dir / "report.txt" 在上述目录下实际指向 /Users/you/project/data/report.txt:
| |
输出如下,同时会生成 data/report.txt:
| |
read_text() 适合体积可控的文本;大文件应逐行读取,避免一次占用过多内存。
12.2 JSON 序列化与反序列化
- 序列化:Python 对象 → 可存储、传输的 JSON 文本。
- 反序列化:JSON 文本 → Python 对象。
函数名带 s 的版本主要处理字符串,不带 s 的版本处理已经打开的文件对象:
| 函数 | 方向 | 输入与结果 |
|---|---|---|
json.dumps(obj) | 序列化 | Python 对象 → JSON 字符串 str |
json.dump(obj, file) | 序列化 | 把 JSON 文本写入文件对象,返回 None |
json.loads(text) | 反序列化 | JSON 字符串 → Python 对象,也支持 bytes、bytearray |
json.load(file) | 反序列化 | 从文件对象读取 JSON,返回 Python 对象 |
dump() / load() 接收文件对象,不是文件路径字符串。 两组方法分别这样使用:
| |
类型限制:JSON 不会自动保存 datetime、Decimal、集合或自定义类,需要先转换为约定的数据结构。
不要对不可信数据使用 pickle。 反序列化恶意 pickle 可以执行代码。
13. 模块、包与程序入口
- 模块:每个
.py文件都是模块。 - 常规包:目录通常包含
__init__.py。 - 导入行为:会执行模块的顶层代码,因此顶层应以定义、常量和必要的轻量初始化为主。
下面只是项目结构示意;可安装包还需要在 pyproject.toml 中声明构建和包发现配置:
| |
脚本入口使用显式的 main(),既方便测试,也避免模块被导入时直接执行业务:
| |
Path.cwd():当前工作目录
cwd 是 current working directory。Path.cwd() 返回当前工作目录的绝对路径对象,通常由启动命令时所在的目录决定。
当前工作目录不保证等于脚本文件所在目录。 相对路径的读写以当前工作目录为基准。
常见导入形式与调用方式如下:
| |
- 避免通配导入:
from module import *会让名称来源不清晰。 - 避免模块重名:不要把文件命名为
random.py、json.py、typing.py等标准库名称,否则可能遮蔽真正的模块。
14. 类与数据类
类把状态和行为组织在一起。不是所有数据都需要类:简单转换可用函数,固定的小型记录可考虑元组或数据类。
@dataclass 自动生成什么?
它是应用在类定义上的装饰器:类定义完成后,根据 name: str 等字段声明补齐常见方法。
| 自动生成的方法 | 用途 |
|---|---|
__init__ | 接收构造参数,保存为实例字段 |
__repr__ | 以可读形式展示对象的字段内容 |
__eq__ | 按字段比较同类型实例是否相等 |
| |
去掉 @dataclass 后有什么差异?
- 构造参数要自己处理:只删装饰器,上面的
Product("Keyboard", ...)会报TypeError。需要手写__init__,给self.name、self.price、self.stock赋值。 - 展示与比较要自己实现:普通类不会默认按这些字段展示内容或比较相等。
它不替你完成什么?
reserve()这类业务方法仍需手写。- 默认不生成大小排序方法。
- 不会自动检查字段类型或业务合法性。
继承与组合:继承表达“是一个”,组合表达“拥有一个”。业务代码通常优先组合;确实需要共享接口和可替换行为时,再使用继承。
15. 迭代器与生成器
- 可迭代对象:能产生迭代器。
- 迭代器:通过
__next__()逐个返回值,耗尽时抛出StopIteration。
日常代码通常使用 for,无需手动调用这些方法。
包含 yield 的函数是生成器函数,调用后返回生成器对象,执行会在迭代时按需推进:
| |
输出:
| |
生成器适合流式处理,使用时注意:
- 只能消费一次:需要重复遍历时,重新创建生成器,或在数据量允许时转成列表。
- 资源生命周期:文件等资源可能在迭代期间一直保持打开,直到退出相应的上下文。
16. 类型标注
类型标注提高可读性,并帮助编辑器和静态类型检查器发现问题。Python 运行时不会根据类型注解强制检查类型。
type UserId = int:给类型起一个业务名称
- 语法来源:Python 3.12 新增的类型别名声明。
- 作用:给
int起别名UserId,让标注表达“这里的整数是用户编号”。 - 边界:没有创建一种新的整数,也不会自动验证编号是否合法。
| |
区分 user_id 与 UserId
user_id是变量:上例绑定的对象实际类型是int。UserId是类型别名对象:不是普通赋值UserId = int,不能当作int()那样调用。
常用标注写法
- 字符串列表:
list[str]。 - 字符串键、整数值的字典:
dict[str, int]。 - 可空类型:
User | None。
类型标注应表达真实契约,不要为了“全部有类型”而使用大量 Any 或复杂泛型。
静态类型检查不能替代外部输入的解析与校验。
17. 调试与测试
17.1 先读完整 traceback
- 最后一行:异常类型和消息。
- 上方调用栈:错误如何到达现场。
应修复根因,不要只在最外层加一个宽泛的 try/except。
常见异常包括:
SyntaxError:语法无法解析。IndentationError:缩进不合法。NameError:名称不存在。TypeError:操作或调用不接受当前类型。ValueError:类型可以接受,但值不合法。KeyError、IndexError:映射键或序列位置不存在。FileNotFoundError:目标文件不存在。
临时观察变量可以使用 print()。日志库的选择与配置不在本篇展开,先掌握如何读出异常位置和原因。
17.2 为行为写测试
标准库 unittest 可以直接起步,项目也可以选择第三方测试框架。测试应覆盖公开行为、边界和失败路径,避免绑定内部实现细节。
先认识示例中的三个名称:
unittest.TestCase:测试基类,提供一组断言方法;继承后即可使用。test_:测试方法的名称前缀,测试运行器会调用这些方法。self:当前测试实例,通过它调用继承的assertEqual()等方法。
把下面内容保存为 test_example.py:
| |
self.assertEqual(actual, expected):比较实际值与期望值
- 相等:断言通过。
- 不相等:抛出断言失败,由测试运行器记录;它不是返回
True/False的判断函数。
这里检查 2 + 3 是否为 5。把期望值改成 6,测试就会失败,并显示 5 != 6。
也可以通过命令行启动测试:-v 展示每项测试的名称,test_example 是去掉 .py 的模块名。
| |
示例输出(unittest 默认写入 stderr,耗时因环境而异):
| |
18. 单词统计小程序
下面的命令行程序综合了路径、函数、类型标注、容器、异常和标准库。
- 输入:UTF-8 文本文件,逐行读取。
- 统计规则:识别由英文字母和内部单引号组成的单词。
- 适用边界:中文等没有空格边界的语言需要分词工具,不能直接套用这个正则表达式。
| |
将程序保存为 word_count.py,并令 sample.txt 内容为 Python is simple. Python is readable.:
| |
输出:
| |
继续练习时可以逐步加入 argparse 参数、停用词、JSON 输出和单元测试。每次只增加一个可验证的能力,比一次堆入框架更容易理解。
19. 学完基础后检查什么
- 能解释名称绑定、可变对象以及常见内置类型的用途。
- 能为字符串、数值和结构化输入选择安全的转换方式,不对外部输入使用
eval()。 - 能熟练使用列表、字典、集合、推导式、
enumerate()和zip()。 - 能写参数清晰的函数,避免可变默认值,并理解局部与外层作用域。
- 能捕获具体异常,用
with管理文件等资源。 - 能创建虚拟环境,通过
python -m pip管理当前项目依赖。 - 能把代码拆成模块,使用
main()入口,并为关键行为编写测试。
评论
使用 GitHub 登录后参与讨论,评论将公开保存在 GitHub。
站长管理删除会同步到 GitHub,无法撤销。
阅读到这里时加载评论。