第 1 卷:执行模型与基础语法

第 1 卷:执行模型与基础语法

学习方式:严格按顺序阅读。每个代码块只使用当前单元或此前单元已经讲过的语法。

本卷项目线:taskflow/milestones/v01_procedural_tasks.py --demo

完成本卷后:进入第 2 卷。

本卷说明

本卷只使用最基础的语句。读完后,你应当能解释变量为何不是"盒子"、可变对象为何会共享、循环如何遍历序列,并能写出一个不使用自定义函数的过程式任务清单。

本卷知识单元

  • [1.1 Python 程序如何运行](#1.1 Python 程序如何运行 "#11-python-%E7%A8%8B%E5%BA%8F%E5%A6%82%E4%BD%95%E8%BF%90%E8%A1%8C")
  • [1.2 名字、对象、类型与赋值](#1.2 名字、对象、类型与赋值 "#12-%E5%90%8D%E5%AD%97%E5%AF%B9%E8%B1%A1%E7%B1%BB%E5%9E%8B%E4%B8%8E%E8%B5%8B%E5%80%BC")
  • [1.3 数字、算术与运算符](#1.3 数字、算术与运算符 "#13-%E6%95%B0%E5%AD%97%E7%AE%97%E6%9C%AF%E4%B8%8E%E8%BF%90%E7%AE%97%E7%AC%A6")
  • [1.4 布尔值、比较与真值](#1.4 布尔值、比较与真值 "#14-%E5%B8%83%E5%B0%94%E5%80%BC%E6%AF%94%E8%BE%83%E4%B8%8E%E7%9C%9F%E5%80%BC")
  • [1.5 字符串、索引、切片与格式化](#1.5 字符串、索引、切片与格式化 "#15-%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%B4%A2%E5%BC%95%E5%88%87%E7%89%87%E4%B8%8E%E6%A0%BC%E5%BC%8F%E5%8C%96")
  • [1.6 字节、编码与文本边界](#1.6 字节、编码与文本边界 "#16-%E5%AD%97%E8%8A%82%E7%BC%96%E7%A0%81%E4%B8%8E%E6%96%87%E6%9C%AC%E8%BE%B9%E7%95%8C")
  • [1.7 列表、元组、range 与解包](#1.7 列表、元组、range 与解包 "#17-%E5%88%97%E8%A1%A8%E5%85%83%E7%BB%84range-%E4%B8%8E%E8%A7%A3%E5%8C%85")
  • [1.8 字典、集合与哈希](#1.8 字典、集合与哈希 "#18-%E5%AD%97%E5%85%B8%E9%9B%86%E5%90%88%E4%B8%8E%E5%93%88%E5%B8%8C")
  • [1.9 可变性、浅复制与哈希](#1.9 可变性、浅复制与哈希 "#19-%E5%8F%AF%E5%8F%98%E6%80%A7%E6%B5%85%E5%A4%8D%E5%88%B6%E4%B8%8E%E5%93%88%E5%B8%8C")
  • [1.10 条件、循环与 loop-else](#1.10 条件、循环与 loop-else "#110-%E6%9D%A1%E4%BB%B6%E5%BE%AA%E7%8E%AF%E4%B8%8E-loop-else")
  • [1.11 输入输出与断言](#1.11 输入输出与断言 "#111-%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E4%B8%8E%E6%96%AD%E8%A8%80")
  • [1.12 过程式任务清单:项目里程碑 v01](#1.12 过程式任务清单:项目里程碑 v01 "#112-%E8%BF%87%E7%A8%8B%E5%BC%8F%E4%BB%BB%E5%8A%A1%E6%B8%85%E5%8D%95%E9%A1%B9%E7%9B%AE%E9%87%8C%E7%A8%8B%E7%A2%91-v01")
  • [1.13 字符串方法全览与文本清洗](#1.13 字符串方法全览与文本清洗 "#113-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%96%B9%E6%B3%95%E5%85%A8%E8%A7%88%E4%B8%8E%E6%96%87%E6%9C%AC%E6%B8%85%E6%B4%97")
  • [1.14 精确小数、分数与特殊浮点值](#1.14 精确小数、分数与特殊浮点值 "#114-%E7%B2%BE%E7%A1%AE%E5%B0%8F%E6%95%B0%E5%88%86%E6%95%B0%E4%B8%8E%E7%89%B9%E6%AE%8A%E6%B5%AE%E7%82%B9%E5%80%BC")
  • [1.15 容器方法与小规模数据处理](#1.15 容器方法与小规模数据处理 "#115-%E5%AE%B9%E5%99%A8%E6%96%B9%E6%B3%95%E4%B8%8E%E5%B0%8F%E8%A7%84%E6%A8%A1%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86")
  • [1.16 过程式状态机与输入校验](#1.16 过程式状态机与输入校验 "#116-%E8%BF%87%E7%A8%8B%E5%BC%8F%E7%8A%B6%E6%80%81%E6%9C%BA%E4%B8%8E%E8%BE%93%E5%85%A5%E6%A0%A1%E9%AA%8C")

1.1 Python 程序如何运行

前置知识:无;只需能打开终端。

概念:Python 先把源码编译成字节码,再由解释器执行。应用开发不需要背字节码,但要知道:文件从上到下执行、缩进属于语法、模块导入会执行顶层代码。

本单元新语法

语法形式 含义
python file.py 运行一个源文件
python -c "print(1)" 执行一小段字符串代码,适合快速验证
# 注释 解释代码意图,不参与执行
缩进 表示代码块;同一块必须使用一致的层级

最小示例

最小示例 1:第一条语句
python 复制代码
# 示例目标:第一条语句
# 前置知识:无;只需能打开终端。
# 代码状态:完整可运行

print("hello")
print(1 + 2)
# 输出:
# hello
# 3

# 示例类型:最小示例
最小示例 2:注释不会改变结果
python 复制代码
# 示例目标:注释不会改变结果
# 前置知识:无;只需能打开终端。
# 代码状态:完整可运行

# 下面只打印一次
print("visible")
# 输出: visible

# 示例类型:最小示例

实际场景示例

命令行脚本、Web 服务启动文件和测试文件都由解释器执行;运行时错误会指出文件和行号。

python 复制代码
# 示例目标:查看失败的位置
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

value = 10
print(value / 0)
# 抛出: ZeroDivisionError: division by zero

# 示例类型:实际场景

关联/组合

组合 1:同一行写多条语句
python 复制代码
# 示例目标:同一行写多条语句
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

name = "ann"; score = 3
print(name, score)
# 输出: ann 3

# 示例类型:关联/组合
组合 2:分步验证
python 复制代码
# 示例目标:分步验证
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

a = 2
b = 3
print(a + b)
# 输出: 5

# 示例类型:关联/组合

易错/易混

易错 1:缩进不一致

错误代码:

python 复制代码
# 示例目标:缩进不一致(错误示范)
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

if True:
    print("yes")
  print("no")

# 示例类型:错误示范

实际结果:IndentationError:缩进层级不一致。

修正代码:

python 复制代码
# 示例目标:缩进不一致(修正)
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

if True:
    print("yes")
print("outside")

# 示例类型:易错修正

原因:Python 用缩进表示块,不能像花括号语言那样自由排版。

易错 2:把注释当输出

错误代码:

python 复制代码
# 示例目标:把注释当输出(错误示范)
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

print("real")
# print("hidden")

# 示例类型:错误示范

实际结果:只输出 real,注释中的 print 不会执行。

修正代码:

python 复制代码
# 示例目标:把注释当输出(修正)
# 前置知识:无;只需能打开终端。
# 代码状态:片段/按前置知识运行

print("real")
print("also real")

# 示例类型:易错修正

原因:以 # 开头的内容只是说明,不能用来运行代码。

练习

  1. 写两行代码,分别输出你的名字和 1 + 1 的结果。
  2. 写一个缩进错误,再改成正确版本,并说明错误类型。

1.2 名字、对象、类型与赋值

前置知识:1.1 能运行代码与写注释。

概念 :赋值让名字指向一个对象。类型属于对象,而不属于名字;同一个名字可以重新指向不同类型的对象。is 判断是否指向同一对象,== 判断值是否相等。

本单元新语法

语法形式 含义
name = value 让名字指向对象
type(obj) 查看对象类型
isinstance(obj, type) 判断对象是否属于某类型
id(obj) 查看对象在当前进程中的身份标识
a is b 判断两个名字是否指向同一对象
a == b 判断两个值是否相等

最小示例

最小示例 1:类型属于对象
python 复制代码
# 示例目标:类型属于对象
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:完整可运行

value = 10
print(type(value))
value = "ten"
print(type(value))
# 输出: <class 'int'>
# 输出: <class 'str'>

# 示例类型:最小示例
最小示例 2:身份与相等
python 复制代码
# 示例目标:身份与相等
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:完整可运行

a = [1, 2]
b = a
c = [1, 2]
print(a is b)
print(a == c)
# 输出: True
# 输出: True

# 示例类型:最小示例

实际场景示例

配置对象、请求数据和缓存值都可能被多个名字共享;排查"改了 A 为什么 B 也变了"时先看身份。

python 复制代码
# 示例目标:共享列表
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

draft = [1, 2]
published = draft
published.append(3)
print(draft)
# 输出: [1, 2, 3]

# 示例类型:实际场景

关联/组合

组合 1:重新绑定不修改旧对象
python 复制代码
# 示例目标:重新绑定不修改旧对象
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

a = 1
b = a
a = 2
print(a, b)
# 输出: 2 1

# 示例类型:关联/组合
组合 2:None 的唯一性
python 复制代码
# 示例目标:None 的唯一性
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

result = None
print(result is None)
# 输出: True

# 示例类型:关联/组合

易错/易混

易错 1:用 is 比较数字内容

错误代码:

python 复制代码
# 示例目标:用 is 比较数字内容(错误示范)
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

a = 1000
b = 1000
print(a is b)

# 示例类型:错误示范

实际结果:结果依赖实现和驻留策略,不能用 is 比较值。

修正代码:

python 复制代码
# 示例目标:用 is 比较数字内容(修正)
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

a = 1000
b = 1000
print(a == b)
# 输出: True

# 示例类型:易错修正

原因:身份与相等是两个问题;除 None/哨兵外通常用 ==。

易错 2:把重新赋值误认为修改

错误代码:

python 复制代码
# 示例目标:把重新赋值误认为修改(错误示范)
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

items = [1]
alias = items
items = [2]
print(alias)

# 示例类型:错误示范

实际结果:输出 1;items 只是重新指向新列表。

修正代码:

python 复制代码
# 示例目标:把重新赋值误认为修改(修正)
# 前置知识:1.1 能运行代码与写注释。
# 代码状态:片段/按前置知识运行

items = [1]
alias = items
items.append(2)
print(alias)
# 输出: [1, 2]

# 示例类型:易错修正

原因:append 修改对象;重新赋值只改变名字的指向。

练习

  1. 让 a 和 b 指向同一个列表,并打印 a is b。
  2. 创建两个内容相同的列表,打印 == 和 is 的结果并解释差异。

1.3 数字、算术与运算符

前置知识:1.2 名字、对象和类型。

概念 :整数支持任意精度;浮点数使用二进制表示,因此金额计算通常不用 float。需要精确十进制时,从 decimal 模块导入 Decimal。/ 总产生浮点结果,// 向下取整,% 取余,** 求幂。

本单元新语法

语法形式 含义
+ - * / 基本四则运算
// 向下取整除法
% 余数
** 幂运算
round(x, n) 按指定位数舍入
abs(x) 绝对值
from decimal import Decimal 从模块导入一个名字

最小示例

最小示例 1:整数与浮点
python 复制代码
# 示例目标:整数与浮点
# 前置知识:1.2 名字、对象和类型。
# 代码状态:完整可运行

print(7 / 2)
print(7 // 2)
print(7 % 2)
print(2 ** 10)
# 输出: 3.5
# 输出: 3
# 输出: 1
# 输出: 1024

# 示例类型:最小示例
最小示例 2:浮点误差
python 复制代码
# 示例目标:浮点误差
# 前置知识:1.2 名字、对象和类型。
# 代码状态:完整可运行

print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)
# 输出: 0.30000000000000004
# 输出: False

# 示例类型:最小示例

实际场景示例

数量、分页、重试次数和预算都可能涉及取整或余数;金额应使用 Decimal 或整数最小单位。

python 复制代码
# 示例目标:分页计算
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

total = 23
page_size = 5
pages = (total + page_size - 1) // page_size
print(pages)
# 输出: 5

# 示例类型:实际场景

关联/组合

组合 1:负数整除
python 复制代码
# 示例目标:负数整除
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

print(-7 // 2)
print(-7 % 2)
# 输出: -4
# 输出: 1

# 示例类型:关联/组合
组合 2:复合赋值
python 复制代码
# 示例目标:复合赋值
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

count = 1
count += 4
print(count)
# 输出: 5

# 示例类型:关联/组合

易错/易混

易错 1:用 float 累加金额

错误代码:

python 复制代码
# 示例目标:用 float 累加金额(错误示范)
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

price = 0.1 + 0.2
print(price == 0.3)

# 示例类型:错误示范

实际结果:输出 False;二进制浮点无法精确表示大多数十进制小数。

修正代码:

python 复制代码
# 示例目标:用 float 累加金额(修正)
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

from decimal import Decimal
price = Decimal("0.1") + Decimal("0.2")
print(price == Decimal("0.3"))
# 输出: True

# 示例类型:易错修正

原因:金额从字符串构造 Decimal,避免先经过 float。

易错 2:把 // 当普通除法

错误代码:

python 复制代码
# 示例目标:把 // 当普通除法(错误示范)
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

print(7 // 2)

# 示例类型:错误示范

实际结果:输出 3,不是 3.5。

修正代码:

python 复制代码
# 示例目标:把 // 当普通除法(修正)
# 前置知识:1.2 名字、对象和类型。
# 代码状态:片段/按前置知识运行

print(7 / 2)
# 输出: 3.5

# 示例类型:易错修正

原因:// 是取整除法;需要普通结果时使用 /。

练习

  1. 用 // 和 % 把 37 分钟拆成分钟与秒。
  2. 用 Decimal 计算 0.1 + 0.2 并打印结果。

1.4 布尔值、比较与真值

前置知识:1.2 对象;1.3 数字。

概念 :比较表达式产生 bool。空容器、0、None、空字符串等为假值;and/or 返回参与运算的对象,不一定返回 True/False。条件判断依赖真值规则。

本单元新语法

语法形式 含义
== != < <= > >= 比较值
and or not 布尔组合或短路选择
bool(x) 显式转换真值
x if condition else y 条件表达式
a < b < c 链式比较

最小示例

最小示例 1:比较与真值
python 复制代码
# 示例目标:比较与真值
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:完整可运行

print(3 > 2)
print(bool(""))
print(bool([0]))
# 输出: True
# 输出: False
# 输出: True

# 示例类型:最小示例
最小示例 2:and/or 的返回值
python 复制代码
# 示例目标:and/or 的返回值
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:完整可运行

print(0 or "fallback")
print("a" and "b")
# 输出: fallback
# 输出: b

# 示例类型:最小示例

实际场景示例

配置默认值、权限判断、表单校验和条件分支都依赖真值;需要区分"缺失"和"合法假值"时要显式判断 None。

python 复制代码
# 示例目标:安全地选择默认端口
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

config = {"port": 0}
port = config["port"] if "port" in config else 8000
print(port)
# 输出: 0

# 示例类型:实际场景

关联/组合

组合 1:链式比较
python 复制代码
# 示例目标:链式比较
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

age = 20
print(18 <= age < 65)
# 输出: True

# 示例类型:关联/组合
组合 2:条件表达式
python 复制代码
# 示例目标:条件表达式
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

score = 75
label = "pass" if score >= 60 else "fail"
print(label)
# 输出: pass

# 示例类型:关联/组合

易错/易混

易错 1:用 or 给 0 设默认值

错误代码:

python 复制代码
# 示例目标:用 or 给 0 设默认值(错误示范)
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

count = 0
print(count or 10)

# 示例类型:错误示范

实际结果:输出 10;这里把合法的 0 当成缺失。

修正代码:

python 复制代码
# 示例目标:用 or 给 0 设默认值(修正)
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

count = 0
value = count if count is not None else 10
print(value)
# 输出: 0

# 示例类型:易错修正

原因:需要区分 None 和合法假值时使用 is None。

易错 2:混淆 and 的返回类型

错误代码:

python 复制代码
# 示例目标:混淆 and 的返回类型(错误示范)
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

value = None and 3
print(value)

# 示例类型:错误示范

实际结果:输出 None,不是 False。

修正代码:

python 复制代码
# 示例目标:混淆 and 的返回类型(修正)
# 前置知识:1.2 对象;1.3 数字。
# 代码状态:片段/按前置知识运行

value = 3 if None is None else 0
print(value)
# 输出: 3

# 示例类型:易错修正

原因:and/or 返回操作数;业务代码不要依赖其隐式类型。

练习

  1. 用条件表达式判断 80 分是否及格。
  2. 分别打印 bool([])、bool([0])、bool(None)。

1.5 字符串、索引、切片与格式化

前置知识:1.1 注释;1.2 名字与对象;1.3 数字。

概念 :字符串是 Unicode 文本序列,不可变。索引从 0 开始,负索引表示从末尾数;切片用 start:stop:step。f-string 用 {} 嵌入表达式,是应用中最常用的格式化方式。

本单元新语法

语法形式 含义
s[i] 取单个字符
s[a:b] 切片,不包含 b
s[::-1] 反向切片
f"{value}" 格式化字符串
s.strip() / split() / join() 常见文本处理

最小示例

最小示例 1:索引与切片
python 复制代码
# 示例目标:索引与切片
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:完整可运行

text = "python"
print(text[0])
print(text[-1])
print(text[1:4])
# 输出: p
# 输出: n
# 输出: yth

# 示例类型:最小示例
最小示例 2:f-string 与格式
python 复制代码
# 示例目标:f-string 与格式
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:完整可运行

name = "ann"
score = 92.345
print(f"{name}: {score:.1f}")
# 输出: ann: 92.3

# 示例类型:最小示例

实际场景示例

日志、API 响应、CSV 文本、提示文案和文件名都涉及字符串;编码边界要显式使用 UTF-8。

python 复制代码
# 示例目标:规范化标签
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

raw = "  Work,Urgent "
parts = []
for part in raw.split(","):
    parts.append(part.strip().lower())
print(parts)
# 输出: ['work', 'urgent']

# 示例类型:实际场景

关联/组合

组合 1:join 拼接
python 复制代码
# 示例目标:join 拼接
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

parts = ["a", "b", "c"]
print("-".join(parts))
# 输出: a-b-c

# 示例类型:关联/组合
组合 2:命名占位符
python 复制代码
# 示例目标:命名占位符
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

item = {"name": "tea", "price": 12}
print("{name}: {price}".format(**item))
# 输出: tea: 12

# 示例类型:关联/组合

易错/易混

易错 1:切片终止值误解

错误代码:

python 复制代码
# 示例目标:切片终止值误解(错误示范)
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

text = "abcd"
print(text[0:2])

# 示例类型:错误示范

实际结果:输出 ab,不包含索引 2。

修正代码:

python 复制代码
# 示例目标:切片终止值误解(修正)
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

text = "abcd"
print(text[0:2])
# 输出: ab

# 示例类型:易错修正

原因:切片 stop 是排他的。

易错 2:用 + 拼接大量文本

错误代码:

python 复制代码
# 示例目标:用 + 拼接大量文本(错误示范)
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

parts = ["a"] * 10000
text = ""
for part in parts:
    text += part

# 示例类型:错误示范

实际结果:结果正确,但循环中反复创建字符串会浪费性能。

修正代码:

python 复制代码
# 示例目标:用 + 拼接大量文本(修正)
# 前置知识:1.1 注释;1.2 名字与对象;1.3 数字。
# 代码状态:片段/按前置知识运行

parts = ["a"] * 10000
text = "".join(parts)
print(len(text))
# 输出: 10000

# 示例类型:易错修正

原因:批量拼接使用 join。

练习

  1. 从 hello world 中取出 world 并转成大写。
  2. 用 f-string 输出保留两位小数的 3.14159。

1.6 字节、编码与文本边界

前置知识:1.5 字符串;1.2 对象。

概念 :str 是文本,bytes 是原始字节。文件、网络和加密处理的是字节;文本进入程序或写出去时必须显式指定编码。encode 把 str 转 bytes,decode 反向转换。

本单元新语法

语法形式 含义
s.encode("utf-8") 文本编码为字节
b.decode("utf-8") 字节解码为文本
b"..." 字节字面量
len(b) 字节数量,不等于字符数量
b[i] 取一个整数字节

最小示例

最小示例 1:编码与解码
python 复制代码
# 示例目标:编码与解码
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:完整可运行

text = "中文"
raw = text.encode("utf-8")
print(raw)
print(raw.decode("utf-8"))
# 输出: b'\xe4\xb8\xad\xe6\x96\x87'
# 输出: 中文

# 示例类型:最小示例
最小示例 2:字节索引
python 复制代码
# 示例目标:字节索引
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:完整可运行

raw = b"abc"
print(raw[0])
print(len(raw))
# 输出: 97
# 输出: 3

# 示例类型:最小示例

实际场景示例

读取上传文件、解析网络协议、计算哈希或写二进制文件时,必须区分文本与字节。

python 复制代码
# 示例目标:计算 UTF-8 字节长度
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

text = "中文"
raw = text.encode("utf-8")
print(len(raw))
# 输出: 6

# 示例类型:实际场景

关联/组合

组合 1:文件编码边界
python 复制代码
# 示例目标:文件编码边界
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

text = "数据"
raw = text.encode("utf-8")
print(raw.decode("utf-8"))
# 输出: 数据

# 示例类型:关联/组合
组合 2:字节长度与字符数量
python 复制代码
# 示例目标:字节长度与字符数量
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

text = "中"
print(len(text), len(text.encode("utf-8")))
# 输出: 1 3

# 示例类型:关联/组合

易错/易混

易错 1:bytes 与 str 不可直接拼接

错误代码:

python 复制代码
# 示例目标:bytes 与 str 不可直接拼接(错误示范)
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

print(b"a" + "b")

# 示例类型:错误示范

实际结果:抛出 TypeError:不能混用 bytes 和 str。

修正代码:

python 复制代码
# 示例目标:bytes 与 str 不可直接拼接(修正)
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

print(b"a" + b"b")
# 输出: b'ab'

# 示例类型:易错修正

原因:先明确当前数据是文本还是字节,再选择转换方向。

易错 2:用错误编码解码

错误代码:

python 复制代码
# 示例目标:用错误编码解码(错误示范)
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

print("中".encode("utf-8").decode("ascii"))

# 示例类型:错误示范

实际结果:抛出 UnicodeDecodeError。

修正代码:

python 复制代码
# 示例目标:用错误编码解码(修正)
# 前置知识:1.5 字符串;1.2 对象。
# 代码状态:片段/按前置知识运行

print("中".encode("utf-8").decode("utf-8"))
# 输出: 中

# 示例类型:易错修正

原因:编码和解码必须使用兼容的字符集。

练习

  1. 把 "data" 编码成 UTF-8,再解码回文本。
  2. 解释为什么 len("中") 和 len("中".encode("utf-8")) 不同。

1.7 列表、元组、range 与解包

前置知识:1.2 名字与对象;1.5 索引与切片。

概念:列表可变,适合同质序列;元组不可变,适合固定记录;range 表示惰性整数序列。索引和切片规则与字符串一致,解包可以把序列元素绑定到多个名字。

本单元新语法

语法形式 含义
[a, b] 列表字面量
(a, b) 元组字面量
range(n) 从 0 到 n-1 的整数序列
items[i] 索引
a, b = pair 解包
*rest 收集剩余元素

最小示例

最小示例 1:列表与元组
python 复制代码
# 示例目标:列表与元组
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:完整可运行

items = [1, 2]
items.append(3)
pair = (10, 20)
print(items)
print(pair[0])
# 输出: [1, 2, 3]
# 输出: 10

# 示例类型:最小示例
最小示例 2:range 与解包
python 复制代码
# 示例目标:range 与解包
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:完整可运行

print(list(range(3)))
first, second = ("a", "b")
print(first, second)
# 输出: [0, 1, 2]
# 输出: a b

# 示例类型:最小示例

实际场景示例

列表用于待办项和查询结果,元组用于坐标、记录和函数返回多值,range 用于计数循环。

python 复制代码
# 示例目标:取首尾元素
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

tasks = ["a", "b", "c"]
first, *middle, last = tasks
print(first, middle, last)
# 输出: a ['b'] c

# 示例类型:实际场景

关联/组合

组合 1:列表排序返回 None
python 复制代码
# 示例目标:列表排序返回 None
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

items = [3, 1, 2]
result = items.sort()
print(result)
print(items)
# 输出: None
# 输出: [1, 2, 3]

# 示例类型:关联/组合
组合 2:列表复制
python 复制代码
# 示例目标:列表复制
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

original = [1, 2]
copy = original.copy()
copy.append(3)
print(original, copy)
# 输出: [1, 2] [1, 2, 3]

# 示例类型:关联/组合

易错/易混

易错 1:单元素元组漏逗号

错误代码:

python 复制代码
# 示例目标:单元素元组漏逗号(错误示范)
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

value = (3)
print(type(value))

# 示例类型:错误示范

实际结果:输出 <class 'int'>;括号不是元组标记。

修正代码:

python 复制代码
# 示例目标:单元素元组漏逗号(修正)
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

value = (3,)
print(type(value))
# 输出: <class 'tuple'>

# 示例类型:易错修正

原因:单元素元组必须保留逗号。

易错 2:切片与索引混淆

错误代码:

python 复制代码
# 示例目标:切片与索引混淆(错误示范)
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

items = [1, 2, 3]
print(items[1])

# 示例类型:错误示范

实际结果:输出 2,不是列表 2。

修正代码:

python 复制代码
# 示例目标:切片与索引混淆(修正)
# 前置知识:1.2 名字与对象;1.5 索引与切片。
# 代码状态:片段/按前置知识运行

items = [1, 2, 3]
print(items[1:2])
# 输出: [2]

# 示例类型:易错修正

原因:索引返回元素,切片返回新序列。

练习

  1. 创建包含 1、2、3 的列表,并追加 4。
  2. 用解包交换两个变量。

1.8 字典、集合与哈希

前置知识:1.7 序列;1.2 对象。

概念:字典保存键值映射,集合保存不重复元素。键和集合元素必须可哈希;可变列表不能作为键。字典查找平均 O(1),集合适合去重和成员测试。

本单元新语法

语法形式 含义
{"k": v} 字典字面量
{a, b} 集合字面量
d[key] 按键取值
d.get(key, default) 安全取值
d[key] = value 新增或覆盖
set(items) 从可迭代对象去重

最小示例

最小示例 1:字典基本操作
python 复制代码
# 示例目标:字典基本操作
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:完整可运行

task = {"id": "t1", "title": "Read"}
print(task["title"])
task["done"] = False
print(task)
# 输出: Read
# 输出: {'id': 't1', 'title': 'Read', 'done': False}

# 示例类型:最小示例
最小示例 2:集合去重
python 复制代码
# 示例目标:集合去重
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:完整可运行

tags = {"work", "work", "home"}
print(tags)
print("work" in tags)
# 输出: {'work', 'home'}
# 输出: True

# 示例类型:最小示例

实际场景示例

任务对象适合用字典表示,标签适合用集合去重,权限和索引适合用集合/字典快速查找。

python 复制代码
# 示例目标:计数
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

words = ["to", "be", "to"]
counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1
print(counts)
# 输出: {'to': 2, 'be': 1}

# 示例类型:实际场景

关联/组合

组合 1:字典合并
python 复制代码
# 示例目标:字典合并
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

defaults = {"priority": "normal"}
override = {"priority": "high"}
print({**defaults, **override})
# 输出: {'priority': 'high'}

# 示例类型:关联/组合
组合 2:集合运算
python 复制代码
# 示例目标:集合运算
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

left = {"a", "b"}
right = {"b", "c"}
print(left | right)
# 输出: {'a', 'b', 'c'}

# 示例类型:关联/组合

易错/易混

易错 1:直接索引缺失键

错误代码:

python 复制代码
# 示例目标:直接索引缺失键(错误示范)
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

task = {}
print(task["title"])

# 示例类型:错误示范

实际结果:抛出 KeyError,后续代码不会执行。

修正代码:

python 复制代码
# 示例目标:直接索引缺失键(修正)
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

task = {}
print(task.get("title", "untitled"))
# 输出: untitled

# 示例类型:易错修正

原因:不确定键存在时使用 get 或显式检查。

易错 2:用列表作为字典键

错误代码:

python 复制代码
# 示例目标:用列表作为字典键(错误示范)
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

data = {[1, 2]: "value"}

# 示例类型:错误示范

实际结果:抛出 TypeError:list 不可哈希。

修正代码:

python 复制代码
# 示例目标:用列表作为字典键(修正)
# 前置知识:1.7 序列;1.2 对象。
# 代码状态:片段/按前置知识运行

data = {(1, 2): "value"}
print(data[(1, 2)])
# 输出: value

# 示例类型:易错修正

原因:键必须不可变且哈希稳定;需要有序记录时用元组。

练习

  1. 创建任务字典,包含 id、title 和 tags,并打印 title。
  2. 用集合去掉 ["a", "b", "a"] 中的重复值。

1.9 可变性、浅复制与哈希

前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。

概念:列表、字典和集合可变,字符串、数字和元组通常不可变。浅复制只复制外层容器,嵌套对象仍共享。对象被放入集合或作为字典键后,不能改变影响哈希的内容。

本单元新语法

语法形式 含义
obj.copy() 列表/字典浅复制
from copy import deepcopy 导入递归复制函数
list(obj) 从可迭代对象创建列表
tuple(obj) 创建元组
hash(obj) 查看可哈希对象的哈希值
可变对象 修改会影响所有指向它的名字

最小示例

最小示例 1:浅复制共享嵌套对象
python 复制代码
# 示例目标:浅复制共享嵌套对象
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:完整可运行

original = {"tags": ["a"]}
copy = original.copy()
copy["tags"].append("b")
print(original)
# 输出: {'tags': ['a', 'b']}

# 示例类型:最小示例
最小示例 2:不可变字符串
python 复制代码
# 示例目标:不可变字符串
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:完整可运行

text = "abc"
changed = text.upper()
print(text, changed)
# 输出: abc ABC

# 示例类型:最小示例

实际场景示例

请求上下文、配置快照和缓存对象必须明确是共享还是复制;浅复制不能隔离嵌套列表。

python 复制代码
# 示例目标:隔离嵌套数据
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

original = {"tags": ["a"]}
copy = {"tags": original["tags"].copy()}
copy["tags"].append("b")
print(original, copy)
# 输出: {'tags': ['a']} {'tags': ['a', 'b']}

# 示例类型:实际场景

关联/组合

组合 1:元组包含可变对象
python 复制代码
# 示例目标:元组包含可变对象
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

item = (["a"],)
item[0].append("b")
print(item)
# 输出: (['a', 'b'],)

# 示例类型:关联/组合
组合 2:字典键不可变
python 复制代码
# 示例目标:字典键不可变
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

key = ("user", 1)
print(hash(key))
# 输出: 一个整数

# 示例类型:关联/组合

易错/易混

易错 1:把 copy 当深复制

错误代码:

python 复制代码
# 示例目标:把 copy 当深复制(错误示范)
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

original = {"items": [1]}
copy = original.copy()
copy["items"].append(2)
print(original)

# 示例类型:错误示范

实际结果:输出包含 1, 2;copy 没有复制嵌套列表。

修正代码:

python 复制代码
# 示例目标:把 copy 当深复制(修正)
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

from copy import deepcopy
original = {"items": [1]}
copy = deepcopy(original)
copy["items"].append(2)
print(original)
# 输出: {'items': [1]}

# 示例类型:易错修正

原因:需要递归隔离时使用 deepcopy,并注意性能和不可复制对象。

易错 2:修改字典键对象

错误代码:

python 复制代码
# 示例目标:修改字典键对象(错误示范)
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

key = [1]
print(hash(key))

# 示例类型:错误示范

实际结果:list 不可哈希,直接抛出 TypeError。

修正代码:

python 复制代码
# 示例目标:修改字典键对象(修正)
# 前置知识:1.2 名字与身份;1.7 列表;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

key = (1,)
print(hash(key))
# 输出: 一个整数

# 示例类型:易错修正

原因:用于哈希表的对象必须保持哈希稳定。

练习

  1. 创建嵌套列表 [[1]] 的浅复制,修改内层并观察原对象。
  2. 用 copy.deepcopy 完成上面的隔离。

1.10 条件、循环与 loop-else

前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。

概念:if 根据条件选择分支;while 根据条件重复;for 遍历可迭代对象。break 提前结束,continue 跳到下一次,for/while 的 else 只在没有 break 正常结束时执行。

本单元新语法

语法形式 含义
if/elif/else 条件分支
while condition 条件循环
for item in iterable 遍历
break 立即退出循环
continue 跳过本轮剩余语句
loop-else 没有 break 时执行

最小示例

最小示例 1:for 与 if
python 复制代码
# 示例目标:for 与 if
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:完整可运行

names = ["ann", "bob"]
for name in names:
    if name == "ann":
        print("first")
# 输出: first

# 示例类型:最小示例
最小示例 2:while 计数
python 复制代码
# 示例目标:while 计数
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:完整可运行

count = 0
while count < 3:
    count += 1
print(count)
# 输出: 3

# 示例类型:最小示例

实际场景示例

批量处理任务、重试、搜索和菜单循环都使用循环;长循环必须有退出条件、上限或超时。

python 复制代码
# 示例目标:搜索并提前退出
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

tasks = ["read", "write", "ship"]
target = "write"
for task in tasks:
    if task == target:
        print("found")
        break
else:
    print("missing")
# 输出: found

# 示例类型:实际场景

关联/组合

组合 1:continue 跳过本轮
python 复制代码
# 示例目标:continue 跳过本轮
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

for number in range(5):
    if number % 2:
        continue
    print(number)
# 输出: 0
# 输出: 2
# 输出: 4

# 示例类型:关联/组合
组合 2:字典遍历
python 复制代码
# 示例目标:字典遍历
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

task = {"title": "read", "status": "pending"}
for key, value in task.items():
    print(key, value)
# 输出: title read
# 输出: status pending

# 示例类型:关联/组合

易错/易混

易错 1:修改正在遍历的列表

错误代码:

python 复制代码
# 示例目标:修改正在遍历的列表(错误示范)
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

items = [1, 2, 3]
for item in items:
    if item == 1:
        items.remove(item)

# 示例类型:错误示范

实际结果:会跳项,结果难以预测。

修正代码:

python 复制代码
# 示例目标:修改正在遍历的列表(修正)
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

items = [1, 2, 3]
filtered = []
for item in items:
    if item != 1:
        filtered.append(item)
items = filtered
print(items)
# 输出: [2, 3]

# 示例类型:易错修正

原因:遍历时删除元素会改变索引;复制筛选后重新绑定。

易错 2:误以为 for-else 总会执行

错误代码:

python 复制代码
# 示例目标:误以为 for-else 总会执行(错误示范)
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

for item in [1, 2]:
    if item == 1:
        break
else:
    print("always")

# 示例类型:错误示范

实际结果:没有输出;break 阻止 else 执行。

修正代码:

python 复制代码
# 示例目标:误以为 for-else 总会执行(修正)
# 前置知识:1.4 布尔与真值;1.7 序列;1.8 字典/集合。
# 代码状态:片段/按前置知识运行

for item in [1, 2]:
    if item == 1:
        break
else:
    print("not reached")
print("after")
# 输出: after

# 示例类型:易错修正

原因:else 表示"未提前退出",不是"循环结束后"。

练习

  1. 打印 0 到 4 中的偶数,只使用 for 和 if。
  2. 在列表中查找 "ship",找到时打印 found,否则打印 missing。

1.11 输入输出与断言

前置知识:1.1 print;1.4 布尔;1.7 序列。

概念 :print 把值写到标准输出;input 从标准输入读取字符串;assert 在条件为假时抛出 AssertionError,适合检查"程序内部应该成立"的条件,不适合处理用户输入错误。

本单元新语法

语法形式 含义
print(*values, sep=..., end=...) 输出文本
input(prompt) 读取一行字符串
int(text) 把字符串转换为整数
assert condition, message 内部条件断言

最小示例

最小示例 1:print 的分隔符
python 复制代码
# 示例目标:print 的分隔符
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:完整可运行

print("a", "b", sep="|")
# 输出: a|b

# 示例类型:最小示例
最小示例 2:assert 成功与失败
python 复制代码
# 示例目标:assert 成功与失败
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:完整可运行

score = 80
assert score >= 0
print("valid")
# 输出: valid

# 示例类型:最小示例

实际场景示例

CLI 工具用 print 输出结果、用 input 读取人工输入;断言适合测试和内部不变量,不能替代正式输入校验。

python 复制代码
# 示例目标:安全的整数转换
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

raw = "42"
if raw.isdigit():
    value = int(raw)
    print(value + 1)
# 输出: 43

# 示例类型:实际场景

关联/组合

组合 1:print 参数
python 复制代码
# 示例目标:print 参数
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

print("x", "y", sep="-")
# 输出: x-y

# 示例类型:关联/组合
组合 2:断言失败
python 复制代码
# 示例目标:断言失败
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

value = 1
assert value == 2, "value must be 2"
# 抛出: AssertionError: value must be 2

# 示例类型:关联/组合

易错/易混

易错 1:把 assert 当成用户校验

错误代码:

python 复制代码
# 示例目标:把 assert 当成用户校验(错误示范)
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

age = -1
assert age >= 0

# 示例类型:错误示范

实际结果:会抛出 AssertionError;生产环境通常不应用断言处理业务错误。

修正代码:

python 复制代码
# 示例目标:把 assert 当成用户校验(修正)
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

age = -1
if age < 0:
    print("age must be non-negative")
# 输出: age must be non-negative

# 示例类型:易错修正

原因:用户错误需要明确的分支/异常和错误响应。

易错 2:input 不会自动转数字

错误代码:

python 复制代码
# 示例目标:input 不会自动转数字(错误示范)
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

text = "18"
print(text + 1)

# 示例类型:错误示范

实际结果:抛出 TypeError:str 和 int 不能相加。

修正代码:

python 复制代码
# 示例目标:input 不会自动转数字(修正)
# 前置知识:1.1 print;1.4 布尔;1.7 序列。
# 代码状态:片段/按前置知识运行

text = "18"
print(int(text) + 1)
# 输出: 19

# 示例类型:易错修正

原因:input 总是返回字符串,需要显式转换和校验。

练习

  1. 用 print(..., sep="-") 输出 a-b。
  2. 写一个断言,要求变量 count 大于等于 0。

1.12 过程式任务清单:项目里程碑 v01

前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。

概念:项目线从一个不使用自定义函数的过程式脚本开始:任务是字典,任务是列表,状态是字符串。先让数据流跑通,再在下一卷用函数和模块重构。

本单元新语法

语法形式 含义
tasks = [] 任务容器
task = {...} 单条任务记录
tasks.append(task) 加入任务
for task in tasks 遍历任务
if task["status"] == ... 筛选状态

最小示例

最小示例 1:创建任务记录
python 复制代码
# 示例目标:创建任务记录
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:完整可运行

task = {"id": "t1", "title": "Read", "status": "pending", "tags": []}
print(task["title"])
# 输出: Read

# 示例类型:最小示例
最小示例 2:遍历任务
python 复制代码
# 示例目标:遍历任务
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:完整可运行

tasks = [{"title": "a"}, {"title": "b"}]
for task in tasks:
    print(task["title"])
# 输出: a
# 输出: b

# 示例类型:最小示例

实际场景示例

这是 TaskFlow 的第一个可运行形态;它不负责持久化、并发或 Web,只验证最基本的任务数据模型。

python 复制代码
# 示例目标:完整过程式流程
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

tasks = []
tasks.append({"id": "t1", "title": "Read", "status": "pending"})
tasks.append({"id": "t2", "title": "Write", "status": "pending"})
for task in tasks:
    if task["title"] == "Write":
        task["status"] = "done"
for task in tasks:
    print(task["id"], task["status"])
# 输出: t1 pending
# 输出: t2 done

# 示例类型:实际场景

关联/组合

组合 1:按状态筛选
python 复制代码
# 示例目标:按状态筛选
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

tasks = [{"title": "a", "status": "done"}, {"title": "b", "status": "pending"}]
for task in tasks:
    if task["status"] == "pending":
        print(task["title"])
# 输出: b

# 示例类型:关联/组合
组合 2:按标签统计
python 复制代码
# 示例目标:按标签统计
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

tasks = [{"tags": {"work"}}, {"tags": {"home"}}, {"tags": {"work"}}]
counts = {}
for task in tasks:
    for tag in task["tags"]:
        counts[tag] = counts.get(tag, 0) + 1
print(counts)
# 输出: {'work': 2, 'home': 1}

# 示例类型:关联/组合

易错/易混

易错 1:在遍历任务时删除

错误代码:

python 复制代码
# 示例目标:在遍历任务时删除(错误示范)
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

tasks = [{"id": "a"}, {"id": "b"}]
for task in tasks:
    if task["id"] == "a":
        tasks.remove(task)
print(tasks)

# 示例类型:错误示范

实际结果:结果可能跳过元素或不符合预期。

修正代码:

python 复制代码
# 示例目标:在遍历任务时删除(修正)
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

tasks = [{"id": "a"}, {"id": "b"}]
filtered = []
for task in tasks:
    if task["id"] != "a":
        filtered.append(task)
tasks = filtered
print(tasks)
# 输出: [{'id': 'b'}]

# 示例类型:易错修正

原因:筛选后重新绑定新列表,避免遍历时修改原列表。

易错 2:把状态写成无法比较的类型

错误代码:

python 复制代码
# 示例目标:把状态写成无法比较的类型(错误示范)
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

task = {"status": 1}
if task["status"] == "done":
    print("done")

# 示例类型:错误示范

实际结果:不报错但永远不命中;类型不一致会导致业务逻辑静默失败。

修正代码:

python 复制代码
# 示例目标:把状态写成无法比较的类型(修正)
# 前置知识:1.2 对象;1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

task = {"status": "done"}
if task["status"] == "done":
    print("done")
# 输出: done

# 示例类型:易错修正

原因:状态字段始终使用同一种表示,后续可用 Enum 强化约束。

练习

  1. 给 tasks 添加一条记录,并打印所有 pending 任务。
  2. 把一条任务的状态从 pending 改为 done,再遍历打印。

1.13 字符串方法全览与文本清洗

前置知识:1.5 字符串、索引和切片;1.10 循环。

概念 :字符串方法返回新字符串,不会修改原对象。清洗文本时通常按"去掉首尾空白、统一大小写、拆分、过滤、重新拼接"的顺序处理;find 找不到返回 -1,index 找不到会抛异常。

本单元新语法

语法形式 含义
strip/lstrip/rstrip 删除首尾/左侧/右侧字符
startswith/endswith 检查前后缀
find/index 查找子串位置
replace(old, new, count) 替换文本
split/rsplit 按分隔符拆分
join(iterable) 用分隔符拼接
casefold 更彻底的大小写折叠
isalpha/isdigit/isalnum 字符类别检查

最小示例

最小示例 1:首尾清理与大小写
python 复制代码
# 示例目标:首尾清理与大小写
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:完整可运行

raw = "  Work ITEM  "
cleaned = raw.strip().casefold()
print(cleaned)
# 输出: work item

# 示例类型:最小示例
最小示例 2:前缀、后缀与查找
python 复制代码
# 示例目标:前缀、后缀与查找
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:完整可运行

filename = "report.csv"
print(filename.endswith(".csv"))
print(filename.find(".txt"))
print(filename.index(".csv"))
# 输出: True
# 输出: -1
# 输出: 6

# 示例类型:最小示例
最小示例 3:拆分与重新拼接
python 复制代码
# 示例目标:拆分与重新拼接
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:完整可运行

raw = " a , b ,, c "
parts = raw.split(",")
cleaned = []
for part in parts:
    value = part.strip()
    if value:
        cleaned.append(value)
print("|".join(cleaned))
# 输出: a|b|c

# 示例类型:最小示例

实际场景示例

表单字段、CSV 文本、搜索关键词和日志消息都需要清洗;清洗后仍要验证长度、格式和业务规则。

python 复制代码
# 示例目标:标签清洗
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

raw = " Work, URGENT,work,  "
tags = []
for part in raw.split(","):
    tag = part.strip().casefold()
    if tag and tag not in tags:
        tags.append(tag)
print(tags)
# 输出: ['work', 'urgent']

# 示例类型:实际场景

关联/组合

组合 1:替换指定次数
python 复制代码
# 示例目标:替换指定次数
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

text = "a-a-a"
print(text.replace("-", "_", 1))
# 输出: a_a-a

# 示例类型:关联/组合
组合 2:对齐与填充
python 复制代码
# 示例目标:对齐与填充
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

print("task".ljust(8, "."))
print("42".zfill(5))
# 输出: task....
# 输出: 00042

# 示例类型:关联/组合
组合 3:字符类别检查
python 复制代码
# 示例目标:字符类别检查
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

print("123".isdigit())
print("abc".isalpha())
print("a1".isalnum())
# 输出: True
# 输出: True
# 输出: True

# 示例类型:关联/组合

易错/易混

易错 1:把 strip 参数当成子串

错误代码:

python 复制代码
# 示例目标:把 strip 参数当成子串(错误示范)
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

text = "prefix-data-suffix"
print(text.strip("prefix-"))

# 示例类型:错误示范

实际结果:输出 ata-suf,strip 会删除参数中任意首尾字符,而不是删除整个子串。

修正代码:

python 复制代码
# 示例目标:把 strip 参数当成子串(修正)
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

text = "prefix-data-suffix"
print(text.removeprefix("prefix-").removesuffix("-suffix"))
# 输出: data

# 示例类型:易错修正

原因:需要删除固定前后缀时使用 removeprefix/removesuffix。

易错 2:忽略 index 的失败

错误代码:

python 复制代码
# 示例目标:忽略 index 的失败(错误示范)
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

text = "report.csv"
position = text.index(".txt")
print(position)

# 示例类型:错误示范

实际结果:抛出 ValueError,程序不会继续。

修正代码:

python 复制代码
# 示例目标:忽略 index 的失败(修正)
# 前置知识:1.5 字符串、索引和切片;1.10 循环。
# 代码状态:片段/按前置知识运行

text = "report.csv"
position = text.find(".txt")
if position == -1:
    print("suffix missing")
# 输出: suffix missing

# 示例类型:易错修正

原因:可预期缺失使用 find 或 in;index 适合"必须存在"的场景。

练习

  1. 把 " A, b, A " 清洗成不重复的小写标签列表。
  2. 用 startswith 和 endswith 同时检查文件是否以 report 开头、以 .csv 结尾。

1.14 精确小数、分数与特殊浮点值

前置知识:1.3 数字与运算符;1.2 对象类型。

概念 :Decimal 用十进制规则计算,适合金额和税率;Fraction 表示精确分数;float 还包含 inf、-inf 和 nan。涉及金额时从字符串构造 Decimal,不要让值先经过 float。

本单元新语法

语法形式 含义
Decimal("0.1") 十进制精确数
quantize(Decimal("0.01")) 按小数位量化
ROUND_HALF_UP 显式指定四舍五入
Fraction(1, 3) 精确分数
float("inf")/float("nan") 无穷与 NaN
value != value NaN 不等于自身
value == float("inf") 判断正无穷

最小示例

最小示例 1:金额量化
python 复制代码
# 示例目标:金额量化
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:完整可运行

from decimal import Decimal, ROUND_HALF_UP
price = Decimal("19.90")
tax = Decimal("0.13")
total = (price * (1 + tax)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(total)
# 输出: 22.49

# 示例类型:最小示例
最小示例 2:精确分数
python 复制代码
# 示例目标:精确分数
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:完整可运行

from fractions import Fraction
value = Fraction(1, 3) + Fraction(1, 6)
print(value, float(value))
# 输出: 1/2 0.5

# 示例类型:最小示例
最小示例 3:特殊浮点值
python 复制代码
# 示例目标:特殊浮点值
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:完整可运行

positive = float("inf")
not_a_number = float("nan")
print(positive > 10 ** 100)
print(not_a_number != not_a_number)
# 输出: True
# 输出: True

# 示例类型:最小示例

实际场景示例

发票、税费、折扣、计量单位和比例计算都要求明确舍入规则;不要只在最后打印时四舍五入。

python 复制代码
# 示例目标:折扣计算
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

from decimal import Decimal, ROUND_HALF_UP
subtotal = Decimal("99.95")
discount = Decimal("0.15")
final = (subtotal * (1 - discount)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(final)
# 输出: 84.96

# 示例类型:实际场景

关联/组合

组合 1:Decimal 与普通数字混合
python 复制代码
# 示例目标:Decimal 与普通数字混合
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

from decimal import Decimal
price = Decimal("10.00")
print(price * 2)
# 输出: 20.00

# 示例类型:关联/组合
组合 2:NaN 比较
python 复制代码
# 示例目标:NaN 比较
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

value = float("nan")
print(value == value)
print(value != value)
# 输出: False
# 输出: True

# 示例类型:关联/组合
组合 3:无穷比较
python 复制代码
# 示例目标:无穷比较
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

print(float("inf") > 10 ** 100)
# 输出: True

# 示例类型:关联/组合

易错/易混

易错 1:用 float 构造 Decimal

错误代码:

python 复制代码
# 示例目标:用 float 构造 Decimal(错误示范)
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

from decimal import Decimal
value = Decimal(0.1)
print(value)

# 示例类型:错误示范

实际结果:输出 0.1000000000000000055511151231257827021181583404541015625,浮点误差被带进来。

修正代码:

python 复制代码
# 示例目标:用 float 构造 Decimal(修正)
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

from decimal import Decimal
value = Decimal("0.1")
print(value)
# 输出: 0.1

# 示例类型:易错修正

原因:金额和用户输入使用字符串构造 Decimal。

易错 2:把 NaN 当普通值

错误代码:

python 复制代码
# 示例目标:把 NaN 当普通值(错误示范)
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

value = float("nan")
if value > 0:
    print("positive")
else:
    print("not positive")

# 示例类型:错误示范

实际结果:输出 not positive,但 NaN 既不是正数也不是负数。

修正代码:

python 复制代码
# 示例目标:把 NaN 当普通值(修正)
# 前置知识:1.3 数字与运算符;1.2 对象类型。
# 代码状态:片段/按前置知识运行

value = float("nan")
if value != value:
    print("invalid")
# 输出: invalid

# 示例类型:易错修正

原因:特殊值先显式检查,再进行大小比较。

练习

  1. 用 Decimal 计算 19.99 * 3,保留两位小数。
  2. 判断 float("inf") 和 float("nan") 的特殊状态。

1.15 容器方法与小规模数据处理

前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。

概念:容器方法按"是否原地修改、是否返回值、找不到时怎样失败"区分。理解这些差异后,很多看似需要复杂算法的数据处理可以用几行完成。

本单元新语法

语法形式 含义
append/extend/insert 列表追加、展开、插入
remove/pop/clear 按值删除/按位置取出/清空
sort/reverse 原地排序/反转,返回 None
get/setdefault/update 字典安全读取/默认值/批量更新
add/discard/union 集合增删与并集
items/keys/values 字典视图

最小示例

最小示例 1:列表增删与排序
python 复制代码
# 示例目标:列表增删与排序
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:完整可运行

items = [3, 1]
items.append(2)
items.extend([4, 5])
items.sort()
print(items)
print(items.pop())
# 输出: [1, 2, 3, 4, 5]
# 输出: 5

# 示例类型:最小示例
最小示例 2:字典批量更新
python 复制代码
# 示例目标:字典批量更新
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:完整可运行

task = {"title": "Read", "done": False}
task.update({"done": True, "priority": "high"})
print(task.get("owner", "unassigned"))
# 输出: unassigned

# 示例类型:最小示例
最小示例 3:集合增删与组合
python 复制代码
# 示例目标:集合增删与组合
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:完整可运行

tags = {"work"}
tags.add("urgent")
tags.discard("missing")
print(tags | {"home"})
# 输出: {'work', 'urgent', 'home'}

# 示例类型:最小示例

实际场景示例

筛选、分组、去重、排序和 Top-K 是应用开发的基础;先选对容器,再考虑算法优化。

python 复制代码
# 示例目标:统计任务标签
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

tasks = [{"tags": {"work", "urgent"}}, {"tags": {"work"}}]
counts = {}
for task in tasks:
    for tag in task["tags"]:
        counts[tag] = counts.get(tag, 0) + 1
print(counts)
# 输出: {'work': 2, 'urgent': 1}

# 示例类型:实际场景

关联/组合

组合 1:setdefault 建分组
python 复制代码
# 示例目标:setdefault 建分组
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

rows = [("work", "read"), ("home", "clean"), ("work", "write")]
groups = {}
for key, value in rows:
    groups.setdefault(key, []).append(value)
print(groups)
# 输出: {'work': ['read', 'write'], 'home': ['clean']}

# 示例类型:关联/组合
组合 2:字典视图
python 复制代码
# 示例目标:字典视图
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

task = {"id": "a", "title": "Read"}
for key, value in task.items():
    print(key, value)
# 输出: id a
# 输出: title Read

# 示例类型:关联/组合
组合 3:集合差集
python 复制代码
# 示例目标:集合差集
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

required = {"id", "title"}
supplied = {"id"}
print(required - supplied)
# 输出: {'title'}

# 示例类型:关联/组合

易错/易混

易错 1:忽略 sort 的返回值

错误代码:

python 复制代码
# 示例目标:忽略 sort 的返回值(错误示范)
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

items = [2, 1]
result = items.sort()
print(result)

# 示例类型:错误示范

实际结果:输出 None;sort 是原地操作。

修正代码:

python 复制代码
# 示例目标:忽略 sort 的返回值(修正)
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

items = [2, 1]
items.sort()
print(items)
# 输出: [1, 2]

# 示例类型:易错修正

原因:原地方法通常返回 None,不要把它赋给变量。

易错 2:remove 不存在的元素

错误代码:

python 复制代码
# 示例目标:remove 不存在的元素(错误示范)
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

items = ["a"]
items.remove("b")

# 示例类型:错误示范

实际结果:抛出 ValueError。

修正代码:

python 复制代码
# 示例目标:remove 不存在的元素(修正)
# 前置知识:1.7 列表/元组;1.8 字典/集合;1.10 循环。
# 代码状态:片段/按前置知识运行

items = ["a"]
if "b" in items:
    items.remove("b")
print(items)
# 输出: ['a']

# 示例类型:易错修正

原因:删除前检查存在性,或使用 discard/条件筛选。

练习

  1. 把两个列表合并、排序并去重。
  2. 用 setdefault 按首字母分组 ["ann", "bob", "amy"]。

1.16 过程式状态机与输入校验

前置知识:1.4 条件;1.10 循环;1.13 字符串方法。

概念:过程式程序用变量保存状态,用 while 维护主循环,用 if/elif 分派命令。输入来自外部时,必须先检查类型、空值和范围;不要用异常或断言代替正常的输入校验。

本单元新语法

语法形式 含义
while True + break 持续处理直到满足退出条件
flag = True/False 保存布尔状态
if/elif/else 按命令分派
s.isdigit()/int(s) 检查并转换整数
sentinel value 特殊结束值
continue 跳过非法输入并继续

最小示例

最小示例 1:有限状态循环
python 复制代码
# 示例目标:有限状态循环
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:完整可运行

status = "pending"
steps = ["start", "finish"]
for step in steps:
    if status == "pending" and step == "start":
        status = "running"
    elif status == "running" and step == "finish":
        status = "done"
print(status)
# 输出: done

# 示例类型:最小示例
最小示例 2:整数输入校验
python 复制代码
# 示例目标:整数输入校验
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:完整可运行

raw = "42"
if raw.lstrip("-").isdigit():
    value = int(raw)
    print(value + 1)
else:
    print("invalid")
# 输出: 43

# 示例类型:最小示例
最小示例 3:哨兵结束
python 复制代码
# 示例目标:哨兵结束
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:完整可运行

commands = ["add", "list", "quit", "ignored"]
for command in commands:
    if command == "quit":
        print("stopped")
        break
    print("handle", command)
# 输出: handle add
# 输出: handle list
# 输出: stopped

# 示例类型:最小示例

实际场景示例

交互式脚本、简单菜单、配置状态机和批处理流程都可以用过程式状态机表达;逻辑增长后应拆成函数。

python 复制代码
# 示例目标:任务状态迁移
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

status = "pending"
commands = ["start", "finish", "finish"]
for command in commands:
    if command == "start" and status == "pending":
        status = "running"
    elif command == "finish" and status == "running":
        status = "done"
print(status)
# 输出: done

# 示例类型:实际场景

关联/组合

组合 1:跳过非法输入
python 复制代码
# 示例目标:跳过非法输入
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

values = ["1", "x", "3"]
valid = []
for raw in values:
    if not raw.isdigit():
        continue
    valid.append(int(raw))
print(valid)
# 输出: [1, 3]

# 示例类型:关联/组合
组合 2:命令分派
python 复制代码
# 示例目标:命令分派
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

command = "list"
if command == "add":
    print("add task")
elif command == "list":
    print("show tasks")
else:
    print("unknown")
# 输出: show tasks

# 示例类型:关联/组合
组合 3:状态校验
python 复制代码
# 示例目标:状态校验
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

current = "done"
next_state = "running"
allowed = {"pending": {"running"}, "running": {"done"}}
print(next_state in allowed.get(current, set()))
# 输出: False

# 示例类型:关联/组合

易错/易混

易错 1:缺少退出条件

错误代码:

python 复制代码
# 示例目标:缺少退出条件(错误示范)
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

status = "pending"
while status == "pending":
    print("waiting")

# 示例类型:错误示范

实际结果:状态永远不改变,程序无限循环。

修正代码:

python 复制代码
# 示例目标:缺少退出条件(修正)
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

status = "pending"
for _ in range(2):
    print("waiting")
    status = "done"
print(status)
# 输出: waiting
# 输出: waiting
# 输出: done

# 示例类型:易错修正

原因:循环必须有可到达的退出路径或上限。

易错 2:只检查 str.isdigit 就接受负数

错误代码:

python 复制代码
# 示例目标:只检查 str.isdigit 就接受负数(错误示范)
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

raw = "-3"
print(raw.isdigit())

# 示例类型:错误示范

实际结果:输出 False,合法负数被拒绝。

修正代码:

python 复制代码
# 示例目标:只检查 str.isdigit 就接受负数(修正)
# 前置知识:1.4 条件;1.10 循环;1.13 字符串方法。
# 代码状态:片段/按前置知识运行

raw = "-3"
text = raw.removeprefix("-")
if text.isdigit():
    value = int(raw)
    print(value)
# 输出: -3

# 示例类型:易错修正

原因:根据业务允许的格式设计校验,而不是套用一个通用判断。

练习

  1. 用 while 和 break 处理 [1, 2, -1, 3],遇到 -1 停止并打印前面的数。
  2. 实现 pending → running → done 的状态迁移,并拒绝非法迁移。

本卷综合实验室:文本任务报告生成器

实验目标

只使用第 1 卷的标量、字符串、列表、字典、循环和条件,把一个原始文本块整理成可读的任务报告。这个实验不定义函数,目的是让你亲手体验"过程式数据流"。

输入与约束

输入是若干行文本,每行格式为 状态|优先级|标题|标签1,标签2。约束如下:

  • 状态只能是 pending 或 done。
  • 优先级只能是 low、normal、high。
  • 空标题、未知状态、未知优先级都要跳过,并记录跳过数量。
  • 标签去空白、转小写、去重,但不能改变原始任务顺序。

完整实现

python 复制代码
# 示例目标:过程式任务报告生成器
# 前置知识:第 1 卷字符串、容器、条件、循环、input/print
# 代码状态:完整可运行

raw_text = """pending|high|  Write report |Work, urgent
done|normal|Read notes|study
bad|high|Broken row|work
pending|low||home
pending|normal|Buy milk|home, HOME
"""

tasks = []
skipped = 0

for raw_line in raw_text.splitlines():
    line = raw_line.strip()
    if not line:
        continue
    parts = line.split("|")
    if len(parts) != 4:
        skipped += 1
        continue
    status, priority, title, tag_text = parts
    status = status.strip().casefold()
    priority = priority.strip().casefold()
    title = title.strip()
    if status not in {"pending", "done"}:
        skipped += 1
        continue
    if priority not in {"low", "normal", "high"}:
        skipped += 1
        continue
    if not title:
        skipped += 1
        continue
    tags = []
    for raw_tag in tag_text.split(","):
        tag = raw_tag.strip().casefold()
        if tag and tag not in tags:
            tags.append(tag)
    tasks.append(
        {
            "status": status,
            "priority": priority,
            "title": title,
            "tags": tags,
        }
    )

counts = {"pending": 0, "done": 0, "high": 0}
for task in tasks:
    counts[task["status"]] = counts.get(task["status"], 0) + 1
    if task["priority"] == "high":
        counts["high"] = counts["high"] + 1

print("Task report")
print("total:", len(tasks), "skipped:", skipped)
print("pending:", counts["pending"], "done:", counts["done"], "high:", counts["high"])
for task in tasks:
    marker = "[x]" if task["status"] == "done" else "[ ]"
    tag_text = ",".join(task["tags"])
    print(marker, task["priority"], task["title"], "tags=" + tag_text)

# 输出:
# Task report
# total: 3 skipped: 2
# pending: 2 done: 1 high: 1
# [ ] high Write report tags=work,urgent
# [x] normal Read notes tags=study
# [ ] normal Buy milk tags=home

逐段解释

  1. raw_text.splitlines() 把多行文本拆成行,strip() 去掉每行首尾空白。
  2. line.split("|") 得到固定四列;长度不对就跳过,而不是让后续索引抛出 IndexError。
  3. casefold() 统一大小写,标签再用一个手写循环去重,保持第一次出现的顺序。
  4. counts.get(key, 0) + 1 是过程式计数的基本模式,避免先判断键是否存在。
  5. 最后只做输出,不修改 tasks,这样调试时可以单独检查每一步的数据形状。

本卷关联

  • 字符串拆分/清理来自 1.13。
  • 字典计数来自 1.8 和 1.15。
  • 条件与循环来自 1.10。
  • 状态校验与哨兵/跳过逻辑来自 1.16。

验收标准

  • 空行不会产生任务。
  • 未知状态、未知优先级、空标题都会计入 skipped。
  • 标签 HOME 与 home 只保留一次。
  • 输出顺序与输入任务顺序一致。
  • 全部代码只使用第 1 卷语法,不导入模块、不定义函数。

复习问答

问:为什么不用 line.split("|", 3)?

答:如果标题中可能包含 |,限制分割次数可以把剩余部分保留在最后一列;本实验的格式固定为四列,所以普通 split 更直观。真实格式应明确是否允许分隔符转义。

问:为什么不能直接 tasks.append 未校验字符串?

答:列表能保存任何对象,但后续代码默认每条记录有 status/title/tags;不校验会让错误推迟到更难定位的地方。

问:为什么不把计数写进输出循环?

答:计数和展示是两个职责,分开后可以单独验证;本卷还没有函数,但仍可以用两个循环表达清晰的数据流。


本卷练习答案

先独立完成练习,再展开答案;答案只使用本卷及之前介绍过的知识。
1.1 Python 程序如何运行 练习答案

练习 1 :写两行代码,分别输出你的名字和 1 + 1 的结果。

python 复制代码
print("ann")
print(1 + 1)

练习 2:写一个缩进错误,再改成正确版本,并说明错误类型。

python 复制代码
if True:
    print("fixed")

1.2 名字、对象、类型与赋值 练习答案

练习 1 :让 a 和 b 指向同一个列表,并打印 a is b。

python 复制代码
a = [1]
b = a
print(a is b)

练习 2 :创建两个内容相同的列表,打印 == 和 is 的结果并解释差异。

python 复制代码
a = [1]
b = [1]
print(a == b, a is b)

1.3 数字、算术与运算符 练习答案

练习 1 :用 // 和 % 把 37 分钟拆成分钟与秒。

python 复制代码
total_seconds = 37 * 60
print(total_seconds // 60, total_seconds % 60)

练习 2 :用 Decimal 计算 0.1 + 0.2 并打印结果。

python 复制代码
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))

1.4 布尔值、比较与真值 练习答案

练习 1:用条件表达式判断 80 分是否及格。

python 复制代码
score = 80
print("pass" if score >= 60 else "fail")

练习 2 :分别打印 bool([])、bool([0])、bool(None)。

python 复制代码
print(bool([]), bool([0]), bool(None))

1.5 字符串、索引、切片与格式化 练习答案

练习 1 :从 hello world 中取出 world 并转成大写。

python 复制代码
text = "hello world"
print(text[6:].upper())

练习 2 :用 f-string 输出保留两位小数的 3.14159。

python 复制代码
value = 3.14159
print(f"{value:.2f}")

1.6 字节、编码与文本边界 练习答案

练习 1 :把 "data" 编码成 UTF-8,再解码回文本。

python 复制代码
raw = "data".encode("utf-8")
print(raw.decode("utf-8"))

练习 2 :解释为什么 len("中") 和 len("中".encode("utf-8")) 不同。

python 复制代码
print(len("中"))
print(len("中".encode("utf-8")))

1.7 列表、元组、range 与解包 练习答案

练习 1:创建包含 1、2、3 的列表,并追加 4。

python 复制代码
items = [1, 2, 3]
items.append(4)
print(items)

练习 2:用解包交换两个变量。

python 复制代码
a, b = 1, 2
a, b = b, a
print(a, b)

1.8 字典、集合与哈希 练习答案

练习 1:创建任务字典,包含 id、title 和 tags,并打印 title。

python 复制代码
task = {"id": "t1", "title": "Read", "tags": {"study"}}
print(task["title"])

练习 2 :用集合去掉 ["a", "b", "a"] 中的重复值。

python 复制代码
print(set(["a", "b", "a"]))

1.9 可变性、浅复制与哈希 练习答案

练习 1 :创建嵌套列表 [[1]] 的浅复制,修改内层并观察原对象。

python 复制代码
original = [[1]]
copy = original.copy()
copy[0].append(2)
print(original)

练习 2 :用 copy.deepcopy 完成上面的隔离。

python 复制代码
from copy import deepcopy
original = [[1]]
copy = deepcopy(original)
copy[0].append(2)
print(original)

1.10 条件、循环与 loop-else 练习答案

练习 1:打印 0 到 4 中的偶数,只使用 for 和 if。

python 复制代码
for number in range(5):
    if number % 2 == 0:
        print(number)

练习 2 :在列表中查找 "ship",找到时打印 found,否则打印 missing。

python 复制代码
tasks = ["read", "ship"]
for task in tasks:
    if task == "ship":
        print("found")
        break
else:
    print("missing")

1.11 输入输出与断言 练习答案

练习 1 :用 print(..., sep="-") 输出 a-b。

python 复制代码
print("a", "b", sep="-")

练习 2 :写一个断言,要求变量 count 大于等于 0。

python 复制代码
count = 1
assert count >= 0

1.12 过程式任务清单:项目里程碑 v01 练习答案

练习 1 :给 tasks 添加一条记录,并打印所有 pending 任务。

python 复制代码
tasks = [{"title": "Read", "status": "pending"}]
for task in tasks:
    if task["status"] == "pending":
        print(task["title"])

练习 2:把一条任务的状态从 pending 改为 done,再遍历打印。

python 复制代码
tasks = [{"title": "Read", "status": "pending"}]
tasks[0]["status"] = "done"
print(tasks[0]["status"])

1.13 字符串方法全览与文本清洗 练习答案

练习 1 :把 " A, b, A " 清洗成不重复的小写标签列表。

python 复制代码
raw = " A, b, A "
tags = []
for part in raw.split(","):
    tag = part.strip().casefold()
    if tag and tag not in tags:
        tags.append(tag)
print(tags)

练习 2 :用 startswith 和 endswith 同时检查文件是否以 report 开头、以 .csv 结尾。

python 复制代码
name = "report.csv"
print(name.startswith("report") and name.endswith(".csv"))

1.14 精确小数、分数与特殊浮点值 练习答案

练习 1 :用 Decimal 计算 19.99 * 3,保留两位小数。

python 复制代码
from decimal import Decimal
print((Decimal("19.99") * 3).quantize(Decimal("0.01")))

练习 2 :判断 float("inf") 和 float("nan") 的特殊状态。

python 复制代码
print(float("inf") > 10 ** 100)
print(float("nan") != float("nan"))

1.15 容器方法与小规模数据处理 练习答案

练习 1:把两个列表合并、排序并去重。

python 复制代码
left = [2, 1]
right = [3, 1]
values = left + right
values.sort()
cleaned = []
for value in values:
    if value not in cleaned:
        cleaned.append(value)
print(cleaned)

练习 2 :用 setdefault 按首字母分组 ["ann", "bob", "amy"]。

python 复制代码
groups = {}
for name in ["ann", "bob", "amy"]:
    groups.setdefault(name[0], []).append(name)
print(groups)

1.16 过程式状态机与输入校验 练习答案

练习 1 :用 while 和 break 处理 [1, 2, -1, 3],遇到 -1 停止并打印前面的数。

python 复制代码
values = [1, 2, -1, 3]
index = 0
while index < len(values):
    value = values[index]
    if value == -1:
        break
    print(value)
    index += 1

练习 2:实现 pending → running → done 的状态迁移,并拒绝非法迁移。

python 复制代码
allowed = {"pending": {"running"}, "running": {"done"}}
status = "pending"
for next_status in ["running", "pending"]:
    if next_status in allowed.get(status, set()):
        status = next_status
    else:
        print("invalid", next_status)
print(status)

本卷验收

  • 能解释本卷每个"本单元新语法"表中的形式。
  • 能独立完成全部练习,并说明错误示例失败的原因。
  • 能运行项目里程碑:taskflow/milestones/v01_procedural_tasks.py --demo。
  • 能说出下一卷开始前必须掌握的三个概念。
相关推荐
总裁余(余登武)1 小时前
K-means算法改进(Python版)
python·算法·kmeans
CaptainT1 小时前
第 2 卷:函数、模块与推导式
python
Shockang1 小时前
项目驱动的 Python 入门
python
余槐i2 小时前
transformers 升级后加载旧权重报错:三处不兼容如何逐一验证
python·大模型·transformers·模型量化·模型推理
CCblandy2 小时前
向量检索 ANN 参数实测:换一种数据分布,recall 掉了 51 个百分点
人工智能·python·算法·自然语言处理·性能优化
云和数据.ChenGuang3 小时前
print(f“a+b=“,result) print(f“a+b={result}“)区别
java·前端·人工智能·python·深度学习
2601_962885723 小时前
如何用 Python 算最大回撤和回撤恢复期?
java·数据库·python
Want5953 小时前
Python大雪纷飞①
开发语言·python·数学建模
程序员清风3 小时前
Python 数据可视化入门:Matplotlib 核心用法
python·信息可视化·matplotlib