Python 3 编程语法规律 —— 小白逆袭手册
从零开始,用奶茶店的故事学 Python 语法。读完能看懂 FastAPI/Django 项目并自己写接口。
创作理念: 假设你要开一家线上奶茶店,从打印菜单到搭建网站,每一步都对应一个 Python 知识点。 读完能干啥: 完整理解 Python 语法 + 看懂 FastAPI/Django 项目 + 自己写接口
📋 故事线速查
| 你开店要做什么 | 对应学什么 | 跳到哪里 |
|---|---|---|
| 管理菜单、订单数据 | Python 数据容器 | 第二章 |
| 处理下单、计算价格 | 函数和类 | 第三章 |
| 让系统自动记录日志 | 装饰器 | 第四章 |
| 同时处理多个订单 | 异步编程 | 第四章 |
| 搭建线上点单 API | FastAPI 项目实战 | 第五章 |
| 换一套技术栈重写 | Django 对比 | 第六章 |
第一章:Python 的核心哲学 —— 万物皆对象
1.1 用「共享文档」理解 Python 的「对象」
| 本节使用 | 类型 | 作用 |
|---|---|---|
list.append(x) | 列表方法 | 往列表末尾添加一个元素 |
print(x) | 内置函数 | 在控制台打印输出内容 |
想象你和室友在手机上共同编辑一份购物清单:
核心规律(用大白话记):
变量名就是文档的分享链接。一份文档可以发给好几个人,不管谁通过谁的链接修改,所有人打开看到的都是同一份内容。
1.2 可变 vs 不可变 —— 在线文档 vs 纸质卡片
| 本节使用 | 类型 | 作用 |
|---|---|---|
id(obj) | 内置函数 | 返回对象在内存中的唯一编号(身份证号) |
type(obj) | 内置函数 | 返回对象的类型 |
list.append(x) | 列表方法 | 往列表末尾添加元素(原地修改) |
Python 里的数据分两种:
一张表记清楚:
1.3 赋值、浅拷贝、深拷贝 —— 一次搞懂(再也不忘)
| 本节使用 | 类型 | 作用 |
|---|---|---|
copy.copy(obj) | copy 模块函数 | 浅拷贝:只复制外层对象,内层对象共享引用 |
copy.deepcopy(obj) | copy 模块函数 | 深拷贝:递归复制所有层级,完全独立 |
dict[key] | 字典取值 | 通过键获取对应的值 |
一句话总结: 赋值 = 同一个文件夹发两个链接;浅拷贝 = 复制了外层但子文件夹共享;深拷贝 = 全部复制一份。
第二章:四种容器 —— 用开餐厅的场景彻底学会
假设你要开一家叫"码力奶茶"的店,下面所有代码都是真实会用到的:
2.1 List(列表)—— 你的点单队列
| 本节使用 | 类型 | 作用 |
|---|---|---|
list.append(x) | 方法 | 往末尾追加一个元素 |
list.insert(i, x) | 方法 | 在索引 i 位置插入元素 |
list.pop(i) | 方法 | 弹出并返回索引 i 的元素,默认最后一个 |
list.remove(x) | 方法 | 删除第一个值为 x 的元素 |
list[索引] | 取值 | 按索引读写元素,支持负数(-1=最后一个) |
list[start:stop:step] | 切片 | 截取子列表 |
[表达式 for 变量 in 可迭代对象] | 列表推导式 | 一行代码生成新列表 |
[表达式 for ... if 条件] | 带过滤的推导式 | 只保留满足条件的元素 |
enumerate(seq, start) | 内置函数 | 遍历时间时给出 (索引, 元素) 对 |
记住: list 就是排队的队伍 —— 有顺序、可重复、可以插队可以离队。
⚠️ list.pop() vs dict.pop() 对比(别搞混了!):
2.2 Dict(字典)—— 你的菜单表
| 本节使用 | 类型 | 作用 |
|---|---|---|
dict[key] | 取值/赋值 | 通过键读写值(键不存在会报错 KeyError) |
dict.get(key, default) | 方法 | 安全取值:键不存在返回默认值,不报错 |
dict.setdefault(key, default) | 方法 | 键不存在就设置默认值,返回最终值 |
dict.pop(key) | 方法 | 删除指定键并返回它的值 |
dict.items() | 方法 | 返回 (键, 值) 对,遍历用 |
dict.keys() | 方法 | 返回所有键 |
dict.values() | 方法 | 返回所有值 |
dict1 | dict2 | 运算符(Python 3.9+) | 合并两个字典,同名键取右边的值 |
记住: dict 就是通讯录/菜单 —— 键(名字)→ 值(信息),查得快,不能有重复的键。
2.3 Set(集合)—— 去重和找共同点
| 本节使用 | 类型 | 作用 |
|---|---|---|
set.add(x) | 方法 | 往集合里加一个元素(重复的自动忽略) |
set1 & set2 | 运算符 | 交集:两个集合都有的元素 |
set1 | set2 | 运算符 | 并集:两个集合所有的元素(去重) |
set1 - set2 | 运算符 | 差集:在 set1 但不在 set2 的元素 |
list(set(seq)) | 转换 | 用 set 去重后再转回 list |
记住: set 就是签到本 —— 不重复、不管顺序、能快速判断"在不在"。
2.4 Tuple(元组)—— 不会变的固定搭配
| 本节使用 | 类型 | 作用 |
|---|---|---|
(值1, 值2, ...) | 语法 | 创建元组(小括号),创建后不可修改 |
a, b = 元组 | 解包赋值 | 把元组里的值分别赋给多个变量 |
return a, b | 语法 | 函数返回多个值,实际打包成元组 |
a, b = b, a | 交换技巧 | 一行交换两个变量的值(背后是元组解包) |
记住: tuple 就是刻在石头上的配方 —— 创建后不能改,适合做函数返回值、字典的键。
第三章:控制流与函数 —— 用电商订单处理学会所有语法
从现在开始,我们用一个完整业务场景来学:顾客下单 → 计算价格 → 判断库存 → 生成订单。
3.1 条件判断 —— if/elif/else
| 本节使用 | 类型 | 作用 |
|---|---|---|
if/elif/else | 语法 | 条件分支:满足哪个条件就执行哪段代码 |
match/case | 语法(Python 3.10+) | 模式匹配:比 if-elif 更清晰的多分支判断 |
_ | 通配符(match) | 匹配任意未列出的情况,相当于 else |
-> 类型 | 类型注解 | 标注函数返回值的类型 |
3.2 循环 —— for 和 while
| 本节使用 | 类型 | 作用 |
|---|---|---|
for 变量 in 可迭代对象: | 语法 | 遍历序列中每个元素 |
while 条件: | 语法 | 条件为真时一直循环 |
enumerate(seq, start) | 内置函数 | 遍历时同时给出索引和元素 |
break | 关键字 | 提前跳出循环 |
for ... else: | 语法 | 循环正常结束(没被 break)时执行 else 块 |
+= / -= | 运算符 | 自增/自减赋值 |
3.3 函数定义 —— 参数传递完全指南
| 本节使用 | 类型 | 作用 |
|---|---|---|
def 函数名(参数): | 语法 | 定义函数 |
*args | 语法 | 接收任意多个位置参数,打包成元组 |
**kwargs | 语法 | 接收任意多个关键字参数,打包成字典 |
-> 类型 | 类型注解 | 标注返回值类型 |
lambda 参数: 表达式 | 语法 | 一行匿名函数 |
sorted(seq, key=函数) | 内置函数 | 排序,key 指定排序依据 |
f"{变量}" | f-string | 格式化字符串,花括号里直接写变量 |
3.4 类定义 —— 用订单系统理解 OOP
| 本节使用 | 类型 | 作用 |
|---|---|---|
class 类名: | 语法 | 定义类 |
class 子类(父类): | 语法 | 继承:子类拥有父类所有方法和属性 |
__init__(self) | 魔术方法 | 初始化方法,创建实例时自动调用 |
self.属性 | 语法 | 实例变量,每个对象独有 |
类.属性 | 语法 | 类变量,所有实例共享 |
__str__(self) | 魔术方法 | print(obj) 时返回的字符串 |
__repr__(self) | 魔术方法 | 调试时 repr(obj) 返回的字符串 |
@classmethod | 装饰器 | 类方法:第一个参数是 cls(类本身) |
@staticmethod | 装饰器 | 静态方法:不需要 self/cls,普通工具函数 |
super().__init__() | 方法 | 调用父类的初始化方法 |
isinstance(obj, 类) | 内置函数 | 判断对象是否属于某个类(含子类) |
OOP 三句话记住:
__init__= 出厂设置,创建时自动执行self= "我自己",每个实例的数据存在 self 里super()= "找我爸",调用父类的方法
第四章:Python 进阶三大件 —— 用奶茶店叫号系统串联
4.1 装饰器 —— 给函数"穿马甲"
| 本节使用 | 类型 | 作用 |
|---|---|---|
@装饰器名 | 语法糖 | 等价于 函数 = 装饰器(函数),给函数加额外功能 |
@wraps(func) | functools 装饰器 | 保留原函数的名字和文档字符串 |
*args, **kwargs | 语法 | 在 wrapper 中透传任意参数给原函数 |
time.time() | time 模块函数 | 获取当前时间戳(秒),用于计时 |
try/except | 语法 | 捕获异常,防止程序崩溃 |
raise | 关键字 | 重新抛出异常 |
本质: 装饰器是一个函数,它接收另一个函数,返回一个"增强版"的函数。就像给手机套壳 —— 手机功能不变,但多了防摔功能。
装饰器 = FastAPI 的灵魂: FastAPI 里
@app.get()、@router.post()全是装饰器!你已经在不知不觉中用了。
4.2 生成器 —— 懒加载的"叫号机"
| 本节使用 | 类型 | 作用 |
|---|---|---|
yield | 关键字 | 暂停函数,返回一个值,下次调用时从此处继续 |
next(生成器) | 内置函数 | 让生成器执行到下一个 yield,返回产出的值 |
(表达式 for 变量 in ...) | 生成器表达式 | 一行创建生成器,用 () 不是 [] |
list(生成器) | 内置函数 | 把生成器剩余元素一次性转成列表 |
for 变量 in 生成器: | 语法 | 遍历生成器,自动调用 next() |
本质: yield 就像一个"暂停按钮",函数执行到 yield 就暂停,下次调用时从暂停处继续。类比:奶茶店叫号机,一次只叫一个号,不会把所有号一次性喊出来。
记忆: yield = "暂停,给你一个值,待会接着跑"。就像奶茶店叫号,叫一个停一下。
4.3 异步编程 —— 同时处理多个订单
| 本节使用 | 类型 | 作用 |
|---|---|---|
async def | 语法 | 定义协程函数(可以被 await 的函数) |
await | 关键字 | 等待一个协程执行完成,不阻塞其他任务 |
asyncio.sleep(n) | asyncio 函数 | 异步等待 n 秒(不阻塞,模拟 IO) |
asyncio.gather(*任务) | asyncio 函数 | 并发执行多个协程,等全部完成 |
asyncio.run(协程) | asyncio 函数 | 运行入口:在普通代码中启动协程 |
time.sleep(n) | time 函数(对比) | 同步等待 n 秒(阻塞,什么都干不了) |
本质: 同步是"做完一件事再做下一件",异步是"在等一件事的时候去做另一件事"。
类比:同步 = 只有一个店员,做完一杯再做下一杯。异步 = 店员在等奶茶机搅拌的时候去收银,搅拌好了回来继续。
一句话:
async= "这个函数可能会等",await= "在这里等一下,但我不傻等,我去干别的"。Web 框架(FastAPI/Django)默认用异步模式处理请求,所以高并发不卡。
第五章:FastAPI 项目实战 —— 从零搭建一个「码力奶茶」点单 API
前面学了语法,现在把它们串起来,搭建一个真实可用的后端 API。
5.1 项目结构 —— 一个请求的完整旅行
请求流程: 用户请求 → router.py 匹配路由 → schemas.py 校验数据 → router.py 处理业务 → models.py 读写数据库 → 返回 JSON
5.2 完整代码 —— 每一行都有注释解释「为什么」
第一步:安装依赖 (requirements.txt)
安装命令(在终端里一行一行敲):
SQLite 不需要单独安装! Python 自带 sqlite3 模块,SQLAlchemy 直接就能用。这就是为什么选 SQLite 作为教学数据库——零配置,开箱即用。
第二步:数据库配置 (database.py)
| 本节使用 | 类型 | 作用 |
|---|---|---|
create_engine(url) | SQLAlchemy 函数 | 创建数据库引擎(连接工厂) |
sessionmaker(...) | SQLAlchemy 函数 | 创建会话工厂,每次请求用新会话 |
DeclarativeBase | SQLAlchemy 基类 | 所有 ORM 模型的基类 |
SessionLocal() | 自定义 | 创建一个数据库会话实例 |
yield | 关键字 | 暂停函数,在 FastAPI 依赖注入中管理资源生命周期 |
try/finally | 语法 | 确保 finally 块中的代码一定执行(如关闭连接) |
第三步:数据模型 (models.py)
| 本节使用 | 类型 | 作用 |
|---|---|---|
Column(类型, ...) | SQLAlchemy | 定义数据库表的列 |
Integer | SQLAlchemy 类型 | 整数列 |
String(n) | SQLAlchemy 类型 | 字符串列,n=最大长度 |
Float | SQLAlchemy 类型 | 浮点数列 |
DateTime | SQLAlchemy 类型 | 日期时间列 |
ForeignKey("表.列") | SQLAlchemy | 外键:引用另一张表的主键 |
primary_key=True | Column 参数 | 设为主键(唯一标识) |
nullable=False | Column 参数 | 不允许为空 |
default=值 | Column 参数 | 默认值 |
relationship("类名", ...) | SQLAlchemy | 定义表之间的关联关系 |
__tablename__ | 类属性 | 指定对应的数据库表名 |
datetime.now(timezone.utc) | datetime 模块 | 获取当前 UTC 时间 |
第四步:数据校验 (schemas.py)
| 本节使用 | 类型 | 作用 |
|---|---|---|
BaseModel | Pydantic 基类 | 所有 Schema 的父类 |
Field(...) | Pydantic 函数 | 定义字段的校验规则 |
... (Ellipsis) | Field 参数 | 表示该字段必填 |
gt / ge | Field 参数 | greater than / greater or equal(大于/大于等于) |
lt / le | Field 参数 | less than / less or equal(小于/小于等于) |
min_length / max_length | Field 参数 | 字符串最小/最大长度 |
default=值 | Field 参数 | 默认值 |
Optional[类型] | typing | 表示字段可以为 None(可选) |
model_config | Pydantic v2 配置 | from_attributes=True 允许从 ORM 对象转换 |
model_validate(obj) | Pydantic 方法 | 从 ORM 对象创建 Schema 实例 |
model_dump() | Pydantic 方法 | 把 Schema 转成字典 |
model_dump(exclude_unset=True) | Pydantic 方法 | 只导出用户明确传了的字段(用于部分更新) |
第五步:API 接口 (router.py)
| 本节使用 | 类型 | 作用 |
|---|---|---|
APIRouter(prefix=, tags=) | FastAPI | 创建子路由器,prefix=给所有路由加前缀 |
@router.get/post/put/delete/patch | 装饰器 | 定义 HTTP 方法的 API 端点 |
Depends(函数) | FastAPI | 依赖注入:自动调用函数获取参数 |
HTTPException(status_code, detail) | FastAPI | 抛出 HTTP 异常,返回错误响应 |
status.HTTP_201_CREATED 等 | FastAPI 常量 | HTTP 状态码常量 |
select(模型) | SQLAlchemy 2.0 | 构建 SELECT 查询语句 |
.where(条件) | SQLAlchemy 查询 | 添加 WHERE 筛选条件 |
.order_by(列) | SQLAlchemy 查询 | 排序 |
.desc() | SQLAlchemy | 降序排列 |
db.execute(语句) | SQLAlchemy | 执行查询 |
.scalars().all() | SQLAlchemy 结果 | 获取所有查询结果 |
db.get(模型, id) | SQLAlchemy | 按主键查询单条记录 |
db.add(obj) | SQLAlchemy | 把对象加入待保存队列 |
db.commit() | SQLAlchemy | 提交事务,真正写入数据库 |
db.refresh(obj) | SQLAlchemy | 刷新对象,获取数据库生成的字段(如 id) |
db.delete(obj) | SQLAlchemy | 标记对象为待删除 |
setattr(obj, key, value) | 内置函数 | 动态设置对象的属性值 |
round(n, 2) | 内置函数 | 四舍五入保留2位小数 |
Counter(可迭代对象) | collections | 统计每个元素出现次数 |
.most_common(n) | Counter 方法 | 返回出现次数最多的前 n 个 |
第六步:应用入口 (main.py)
| 本节使用 | 类型 | 作用 |
|---|---|---|
FastAPI(title=, version=, lifespan=) | FastAPI | 创建应用实例 |
@asynccontextmanager | contextlib 装饰器 | 把生成器函数变成异步上下文管理器 |
Base.metadata.create_all(bind=引擎) | SQLAlchemy | 自动创建所有数据库表 |
app.include_router(路由器) | FastAPI 方法 | 注册子路由器 |
@app.get("/") | 装饰器 | 定义根路径接口 |
db.add_all([obj1, obj2]) | SQLAlchemy | 批量添加多个对象 |
db.query(模型).count() | SQLAlchemy(旧API) | 查询表中记录总数 |
5.3 零基础启动指南 —— 从零到 API 跑起来
步骤 1:确认环境
步骤 2:创建项目文件
在桌面上创建一个文件夹,把上面 6 个文件的代码分别复制进去:
注意: 文件名必须一模一样!Python 的
import是按文件名查找的,from database import Base会找database.py这个文件。
步骤 3:安装依赖
步骤 4:启动服务器
启动成功后你会看到:
这时候项目文件夹里多了一个文件:
mali_tea.db— 这就是你的 SQLite 数据库!所有数据都存在这里面
步骤 5:测试 API
打开浏览器,访问:
| 地址 | 功能 |
|---|---|
http://127.0.0.1:8000 | 欢迎页 |
http://127.0.0.1:8000/docs | 交互式 API 文档(重点!) |
http://127.0.0.1:8000/redoc | 另一种风格的文档 |
在 /docs 页面上直接测试:
- 点击
GET /api/v1/drinks→ 点Try it out→ 点Execute→ 看到返回 5 款奶茶的 JSON 数据 ✅ - 点击
POST /api/v1/drinks→Try it out→ 填数据:→ 点Execute→ 返回新创建的饮品 ✅ - 点击
POST /api/v1/orders→Try it out→ 填数据:→ 点Execute→ 返回订单详情,库存自动扣减 ✅
步骤 6:查看数据库(可选)
遇到问题?
| 报错 | 原因 | 解决 |
|---|---|---|
ModuleNotFoundError: No module named 'fastapi' | 依赖没装 | 重新执行 pip install -r requirements.txt |
Address already in use | 端口被占用 | 换端口:uvicorn main:app --reload --port 8001 |
sqlite3.OperationalError | 数据库文件损坏 | 删掉 mali_tea.db 重启,会自动重建 |
5.4 FastAPI 项目核心规律总结
| 你在做什么 | 对应代码 | 关键概念 |
|---|---|---|
| 定义数据库表 | models.py 里的 class Drink(Base) | SQLAlchemy ORM |
| 校验输入输出 | schemas.py 里的 class DrinkCreate(BaseModel) | Pydantic |
| 定义 API 端点 | router.py 里的 @router.post("/orders") | 路由装饰器 |
| 获取数据库连接 | db = Depends(get_db) | 依赖注入 + yield |
| 统一返回格式 | return ApiResponse(data=...) | 约定优于配置 |
第六章:Django 对比 —— 同一家店用不同框架怎么写
假设"码力奶茶"火了,技术主管说要用 Django 重写。别慌,核心逻辑完全不变!
6.1 概念映射速查表
| 你要做的事 | FastAPI | Django |
|---|---|---|
| 创建项目 | 手动建文件 | django-admin startproject |
| 定义表结构 | models.py + SQLAlchemy Column | models.py + Django models.Field |
| 校验数据 | schemas.py + Pydantic BaseModel | serializers.py + DRF Serializer |
| 定义接口 | @router.get("/path") 装饰器 | urls.py + views.py |
| 操作数据库 | db.add(obj) + db.commit() | obj.save() |
| 依赖注入 | Depends(get_db) | 框架内置 request 对象 |
| 启动命令 | uvicorn main:app | python manage.py runserver |
6.2 Django 版本代码对照
| Django 本节使用 | 作用 | 对应 FastAPI 的什么 |
|---|---|---|
models.CharField(max_length=n) | 字符串字段 | SQLAlchemy Column(String(n)) |
models.FloatField() | 浮点数字段 | SQLAlchemy Column(Float) |
models.IntegerField(default=0) | 整数字段 | SQLAlchemy Column(Integer, default=0) |
models.DateTimeField(auto_now_add=True) | 创建时自动设时间 | SQLAlchemy default=datetime.now |
models.DateTimeField(auto_now=True) | 更新时自动设时间 | SQLAlchemy onupdate=datetime.now |
models.ForeignKey(模型, on_delete=) | 外键 | SQLAlchemy ForeignKey("表.id") |
obj.save() | 保存到数据库 | SQLAlchemy db.add(obj); db.commit() |
Model.objects.all() | 查询全部 | SQLAlchemy select(模型) |
Model.objects.filter(条件) | 条件筛选 | SQLAlchemy .where(条件) |
Model.objects.get(id=n) | 按主键查一条 | SQLAlchemy db.get(模型, id) |
Model.objects.create(**data) | 创建并保存 | SQLAlchemy db.add(); db.commit() |
serializers.ModelSerializer | ORM 序列化器 | Pydantic BaseModel + from_attributes=True |
serializer.is_valid() | 校验数据 | Pydantic 自动校验(无需手动调用) |
serializer.save() | 校验通过后保存 | 手动 db.add() + db.commit() |
APIView + def get/post(self, request) | 视图类 | @router.get/post 函数 |
Response(data, status=) | 返回 JSON 响应 | return dict / ApiResponse |
path("url/", View.as_view()) | 路由注册 | @router.get("/url") |
6.3 核心发现:不变的 80%
第七章:Python 项目通用模式 —— 换框架不换代码的秘密
7.1 任何 Web 框架都逃不过这五件事
关键洞察: ③ 是纯 Python 代码,④ 虽然跟 ORM 相关但概念相同(都是"对象存到数据库"),真正框架相关的只有 ① ② ⑤!
7.2 写接口的万能模板
7.3 快速上手新框架的 4 个问题
拿到一个没见过的 Python Web 框架,先找这四个答案:
第八章:Python 3 十大黄金法则
| 法则 | 内容 | 一句话口诀 |
|---|---|---|
| 法则一 | 万物皆对象,变量是标签不是盒子 | 共享文档链接,谁改大家都变 |
| 法则二 | 可变/不可变是理解一切的关键 | 石头不能改,箱子可以装 |
| 法则三 | 四种容器:list有序 dict映射 set去重 tuple不变 | 排队/通讯录/签到本/配方 |
| 法则四 | 函数参数顺序:必填→默认→*args→关键字→**kwargs | 从硬到软排列 |
| 法则五 | 装饰器 = 给函数加前后处理 | 手机套壳,功能不变多保护 |
| 法则六 | yield = 懒加载,按需产出 | 叫号机,一次叫一个 |
| 法则七 | async/await = 不等人的 IO | 等奶茶机的时候去收银 |
| 法则八 | Web框架五步:路由→校验→业务→存库→返回 | 记住模板填空就行 |
| 法则九 | 声明式编程:声明"要什么",框架帮你"怎么做" | 点菜,不用进厨房 |
| 法则十 | 80%业务代码是纯Python,跟框架无关 | 换了框架,核心代码不动 |
🎯 下一步行动指南
按照这个顺序,从看懂到能写:
最后记住: Python 学不会不是因为难,是因为没找到规律。现在规律都在这里了——像开奶茶店一样学,每一步都有真实场景对应。代码抄一遍,改一改,跑一跑,三遍就会了!
