2022-回放-03:作业讲评、生成器、二进制数据与第二次作业 Base64(第三次直播回放)¶
内容梗概¶
这是第三次直播回放课(约 1 小时 52 分钟),内容是"第一次作业讲评 + 程序结构章节补充 + 布置第二次作业"。老师先随机点评了几位同学的 ROT13 作业(借函数参数的位置/关键字调用讲评写法),随后现场用自己写的测试脚本批量测试全班作业(还闹了测试条件写反的"翻车")。之后补充讲解程序结构章节的遗留内容:in 关键字、循环控制、pass 的开发占位用法,并重点引入生成器(yield)与列表的本质区别、bytes 二进制数据类型、用字典替代 switch 实现多分支。最后布置第二次作业:从零实现 Base64 编码解码并自带测试。结尾预告了后续课程安排(数据类型进阶 + 输出合并、字符画实验、加快看视频进度)。
知识点详解¶
函数参数的调用方式(约 00:00–03:00)¶
- 以带一个必选参数 + 三个默认参数(
xxx=...形式)的函数为例,讲解三种调用方式: - 只传必选参数,可选参数全用默认值;
- 指名道姓用关键字传参(
参数名=值); - 按位置顺序传参。
- 规则:关键字参数必须跟在位置参数后面。一旦某个参数用了
名字=值的形式,它后面的参数也必须用关键字形式;位置传参就要全部按顺序。可选参数的默认值写函数定义里,调用时按位置传可省略base=。 - 讲这个的原因:将来读别人代码会遇到各种"稀奇古怪"的合法写法,要看得懂。
第一次作业讲评(约 03:00–12:00)¶
- 随机抽同学代码点评,共性问题:
- 代码格式:操作符(如
,、=) 两侧要空格;顶格语句该顶格;合理空行。 if (条件):加括号是 C 程序员遗留习惯,Python 里不需要(但也能正常工作)。- 字符串引号风格统一问题(承接上节课)。
- 好的写法示例:decode 里用
for循环 + 切片s[i:i+2]每次取两个字符、i += 2步进;int(x, 16)转十六进制。 - 老师现场用自动测试脚本批量测全班作业:随机生成多组数据逐一比对,打印错误项——结果"全军覆没",排查后发现是**老师自己测试脚本的判断条件写反了**(在相等时打印错误),改正后全部通过。教训:测试代码本身也可能错;也顺便演示了做日志时应把错误样例打印出来。
- 小技巧:网页内容禁止复制时,右键"检查"在开发者工具里选中 HTML 直接复制。
程序结构章节补充(约 12:30–28:00)¶
- 开场示例:一个"点不到的表白按钮"小程序(你鼠标永远点不到"不"),用到循环不断生成随机位置 + 分支,以及 Python 内置 GUI 库(tkinter);说明 Python 写带界面的程序很简单,代码细节自己查文档。
in关键字在分支判断中的用法(复习):if name in student_list:,后面可以是列表、字符串、字典、元组等序列,用于成员查找;经典算法题"长字符串中查找子串"也可用in。- Python 没有
do while(C 语言有),只有while;两者相似,只是条件位置前后不同,改写很容易。 break/continue/else与 C 语言类似不展开。pass的开发用法:先把程序框架(函数名、流程)搭好,未实现的函数体写pass占位避免报错,之后逐个填充并测试。这是正式开发中非常常见的流程——上节课作业的"完形填空"框架就是这个用法的实例。- 老师强调"开发能力"的重要性:信安专业同学专业课强但开发常欠缺,而行业越来越多工具/任务要自己开发,不能只学语法,要学会"设计 → 拆解 → 搭结构 → 填充 → 测试"的完整开发思路。
生成器(generator)与 yield(约 28:00–46:20)¶
- 以
def reverse(data):为例:把一个序列倒序输出。循环写法很巧妙——for index in range(len(data)-1, -1, -1):(start 为最后一个下标=长度-1,end 写 -1 才能取到下标 0,step 为 -1 倒着走),每次yield data[index]。 - 用
yield返回的"函数"叫**生成器**。老师本想强调"生成器不是函数",现场print(type(...))验证时翻车:含 yield 的定义**确实是函数**(type 是 function),但它的**返回值类型是 generator**;而普通函数返回什么类型就是什么类型(如 list)。结论:函数是个大集合,生成器是一种特殊的函数(返回生成器迭代器的函数),详细内容在"函数进阶与类"章节展开。 - 生成器与列表的核心区别:列表在创建时就初始化、占好全部内存(如存 1 到 1024 每个元素 2 字节,空间已分配);生成器返回时并没有算出所有元素,在你迭代循环的过程中才逐个计算——是节约运行时内存的手段。
- 应用场景:内存受限的嵌入式/板卡开发(树莓派等,板卡可能只有 16KB 内存),要生成 0 到一千万的序列时必须用生成器而非列表。普通电脑开发对此不敏感。
- 又一次翻车:
range也不是生成器——type(range(10))显示它是一个单独的range类(可迭代对象),与生成器无关。老师借此灌"终生学习、学了忘忘了学"的鸡汤,并提醒学生要敢于质疑老师、一切用代码跑一遍验证。
二进制数据:bytes 与 bytearray(约 57:40–1:16:00)¶
- 为什么讲:Base64 既能处理文本数据也能处理二进制数据(如 0x00 这种在字符串里无意义的字节,在二进制视角下与其他字节完全平等),所以作业要求用随机二进制序列做测试。
- Python 的序列分**可变序列**(列表,可修改元素)和**不可变序列**(元组,元素不可改)。"数据类型进阶"一章会系统讲。
bytes对象:**不可变**的字节序列;想修改就用bytearray(可变字节序列)。- bytes 与字符串表面很像,本质是两种东西。演示:用
open("1.png")文本模式读图片文件报UnicodeDecodeError——Python 3 默认把读入的二进制用 UTF-8 解码成字符串,而图片里大量字节无法解码(人类可读字符有限、字节组合无限)。二进制视角讲解:Unicode是字符集,UTF-8 是它的一种编码方式。 - 正确做法:
open("1.png", "rb")以二进制模式读,f.read(10)读 10 字节,得到 bytes 对象。 - bytes 打印出来的两种形态:能解码成可打印字符的直接显示字符(如
P、N、G,以及\r回车、\n换行这类 ASCII 控制符);解码不出来的显示十六进制转义(如\x89)。文件操作与编码细节在"文件与目录"章节展开。 - 随机生成二进制序列:
random.randbytes(n)生成 n 个随机字节(random 模块接口)。
字典与多分支(Python 为什么没有 switch)(约 1:22:50–1:43:00)¶
- Python 没有
switch/case语句。以"成绩等级"为例:60 以下不及格、60–80 中、80–90 良、90 以上优,用if/elif嵌套写很啰嗦;input("请输入...")函数接收用户输入(类似 scanf)。 - 改用字典:字典是
key: value形式的数据类型,定义好规则字典后,用 key 取 value 一句话替代多重分支(如grade为 key、分数区间描述为 value)。前提是要先定义好这个映射字典,且"得到 key 的过程"不能太复杂(分数区间比较这种就不适合直接用字典做 key)。 - 字典的 key 必须是不可变对象:字符串、数字、元组、bytes 都可以;列表、bytearray 是可变的,不能做 key。value 可以是任何对象:数字、列表、另一个字典、自定义类的实例都行,"包罗万物"。字典可以有 0 组、1 组或多组 key-value。
- 字典火的原因——JSON(JavaScript Object Notation):起源于 JavaScript、如今 Web 领域最通用的数据传输格式,本身就是 key-value 字典形式。现场用浏览器开发者工具演示:知乎页面下翻加载新内容是通过 fetch/xhr 请求向服务器要数据,返回的基本都是 JSON(能看到
data、paging等 key)。Python 自带json库,一句话就能把 HTTP 拿到的 JSON 转成字典(后话)。 - 由"用字典改写分支"引申:数据结构课程的意义——数据结构为程序服务,选对结构能改变代码的整体逻辑,让程序更工整明晰。
示例与演示¶
- 作业讲评演示:逐份点评同学 ROT13 作业的代码风格与写法;现场运行自动测试脚本(随机生成数据 → encode → decode → 比对,统计对错数量),并现场修复测试脚本自身的 bug。
- reverse 生成器演示:对比"yiled 版"与"append 拼列表版"两种写法,
type()验证返回值类型分别为 generator 和 list;type(range(...))验证 range 是独立类。 - bytes 演示:同一段 "ABC" 分别存为字符串和 bytes 对比;
open("1.png")文本读报错 → 改rb成功读出,逐字节讲解\x89、PNG、\r\n的显示形态。 - 字典替代 switch 演示:
input输入等级 → 字典 key 取 value 输出对应分数区间(中途发现"分数→等级"方向不适合用字典,及时换成"等级→区间"方向)。
第二次作业:手写 Base64 编码与解码(约 47:50–58:30,1:16:00–1:22:00)¶
- 背景:Base64 是经典编码算法(CTF 常见),原理在课程视频里已讲,本课不再重复,自己回去看视频。算法本身要考虑的分支和异常处理很精巧,值得亲手实现。
- 作业要求(比第一次作业提高一档:空仓库、无模板代码,全自己写,且首次对仓库内容提出明确要求):
- fork 老师建的空仓库,只提交**一个 .py 文件**:不要用中文命名、不要没有后缀名,建议叫
homework.py;不要叫base64.py——会与 Python 内置的 base64 库冲突导致奇怪问题。 - 文件里实现两个函数:
encode和decode。 - 必须自己写测试:在
if __name__ == "__main__":下写循环,随机生成**二进制序列**(如random.randbytes(random.randint(10, 20)))→ 丢进encode→ 再decode→ 比对与原始输入是否完全一致,全部通过才算成功。老师拿到代码会直接python homework.py跑测试并核对对错数量。 - 接口约定:
encode输入 bytes、输出 str;decode输入 str、输出 bytes。输入 bytes 经编码解码后应还原出相同的 bytes。 - 易错点——补等于号:待编码字节数不是 3 的倍数时,编码结果末尾必须补
=(可能一个或两个)。往年很多同学漏补,导致解码出错,本次强制要求。 - 严禁使用内置 base64 库,必须自己实现算法,否则作业没意义。
- 提醒:不要去找上一届同学的作业抄(内容与上届相同,但要求不同),后续实验作业(找文件特征、博客开发、爬虫等)都会换题。
重点与难点¶
- 位置参数与关键字参数的混用规则(关键字必须在位置之后)。
- 生成器的本质:含 yield 的也是函数,但返回 generator 对象;惰性计算省内存,与列表的本质区别在于是否一次性初始化。
range是可迭代的独立类,不是生成器也不是函数。- bytes vs str:文本模式读二进制文件会因 UTF-8 解码失败报
UnicodeDecodeError;读二进制一律"rb"。 - 字典 key 的可变性限制(不可变对象才能做 key),这是常考易错点。
- Base64 作业三大坑:文件名撞库、漏补
=、没有覆盖二进制数据的测试。 - 测试代码也要验证(老师的测试脚本条件写反,差点"误杀"全班)。
关联内容¶
- 承接回放-02:讲评的正是第一次作业(ROT13 + ASCII 十六进制);
in、列表推导式、if __name__ == "__main__"等均是上节课内容的复习与应用。 - 生成器、迭代器的系统讲解在第 11 章"函数进阶与类";编码/字符集、文件读写(open、rb、encoding)在"文件与目录"章节展开。
- 字典、元组、集合、range 的系统讲解在"数据类型进阶"视频(下下周与"输出"章节合并上线下课);JSON 与字典的转换、fetch/xhr 分析为后面"爬虫"章节(分析知乎接口)做铺垫。
- 预告:后续课程加快看视频进度(可两倍速/快进),第 7 章起内容更偏实战,线下课将做"图片转字符画(ASCII 图)"等小实验——用本阶段所学知识即可完成。
- 前置知识:C 语言的循环/分支/函数基础、ASCII 码与进制转换(C 语言课已学)。