知识点思维导图
21 个知识节点
参考资料
Python(05) - 控制流与函数
读完后,你应能完成以下任务:
- 绘制“Python(05) - 控制流与函数 / 前端锚点”的关键对象与数据流,解释“for 默认就是 for...of(遍历值,不是索引)。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(05) - 控制流与函数 / 控制流:先划清和 JS 的差异”设计正常与异常输入,验证“这是 Python 第一眼最大的不同。”,输出首个偏差位置与回归测试结果。
- 实现“Python(05) - 控制流与函数 / for:默认是 for...of,要索引用 enumerate”的最小代码或配置,检验“Python 的 for 没有 for (let i = 0; ...) 这种 C 式写法,默认就是遍历值,等价于 JS 的 for...of。”,输出命令、结果与 Diff,并说明不适用边界。
控制流(if/for/while)你在 JS 里已经写过无数遍,这篇只需关注「哪里不一样」。真正的重点是 Python 的函数:
def、默认参数、*args/**kwargs——这套参数玩法比 JS 灵活得多,也是后面读 FastAPI / openai SDK 源码绕不开的基础。
一、前端锚点
控制流部分,Python 和 JS 心智模型几乎一致,差异只在「语法皮肤」:没有花括号,靠缩进分块;for 默认就是 for...of(遍历值,不是索引)。
函数部分差异更大,先记住一句类比:Python 的函数参数 ≈ JS 函数参数 + 默认值 + 解构 + 剩余参数,但全部内置且更显式。*args ≈ JS 的 ...rest,**kwargs ≈ 把一个对象「展开」成具名参数。
二、控制流:先划清和 JS 的差异
2.1 没有花括号,缩进就是代码块
这是 Python 第一眼最大的不同。JS 用 {} 圈代码块,Python 用冒号 + 缩进。
// JavaScript:花括号分块
if (age >= 18) {
console.log("成年")
} else {
console.log("未成年")
}
关键差异:
- 行尾必须有
:,下一行必须缩进(统一 4 空格,别和 Tab 混用,否则报IndentationError) - 缩进是语法不是风格,缩进错了直接报错(JS 缩进只是好看)
- 没有分号(写了也不报错,但不符合习惯)
2.2 条件判断:elif、布尔运算符是英文单词
// JavaScript
if (score >= 90) {
grade = "A"
} else if (score >= 60) {
grade = "B"
} else {
grade = "C"
}
const ok = isVip && age > 18 || isAdmin // && || !
对照表:
| JS/TS | Python | 说明 |
|---|---|---|
else if |
elif |
合并成一个关键字 |
&& |
and |
逻辑与 |
|| |
or |
逻辑或 |
!x |
not x |
逻辑非 |
a === b |
a == b |
Python 无 ===,== 即比值 |
cond ? a : b |
a if cond else b |
三元,顺序不同 |
注意三元表达式的语序——Python 把「真值」写在最前面:
2.3 for:默认是 for...of,要索引用 enumerate
Python 的 for 没有 for (let i = 0; ...) 这种 C 式写法,默认就是遍历值,等价于 JS 的 for...of。
// JavaScript
const fruits = ["apple", "banana", "cherry"]
for (const f of fruits) {
console.log(f)
}
for (let i = 0; i < 5; i++) {
console.log(i)
}
需要同时拿索引和值时,用内置的 enumerate,对标 JS 的 arr.entries():
// JavaScript:要索引时
for (const [i, f] of fruits.entries()) {
console.log(i, f)
}
遍历字典(≈ JS 对象)时,用 .items() 同时拿键值:
| 需求 | JS | Python |
|---|---|---|
| 遍历值 | for (const x of arr) |
for x in arr |
| 遍历 N 次 | for (let i=0; i<n; i++) |
for i in range(n) |
| 带索引 | arr.entries() |
enumerate(arr) |
| 遍历对象键值 | Object.entries(obj) |
dict.items() |
2.4 while:基本一样,但没有 do-while
注意:Python 没有 ++ / --,也没有 do...while。需要「至少执行一次」时,用 while True + break 模拟。
三、函数 def:重点章节
3.1 基本定义
// JavaScript
function add(a, b) {
return a + b
}
const add2 = (a, b) => a + b // 箭头函数
关键差异:
- 用
def,函数体靠缩进 lambda是 Python 的「箭头函数」,但只能放一个表达式,不能写多行逻辑——所以 Python 里写复杂逻辑一律用def,lambda 只用于sorted(key=...)这类一次性小函数
3.2 类型注解(可选,但后端常写)
Python 支持类型注解,长得像 TS,但默认不强制检查(运行时不报错,靠 IDE / mypy / Pydantic 检查)。FastAPI 正是靠这套注解做参数校验。
// TypeScript:类型是强制的
function greet(name: string): string {
return `Hello, ${name}`
}
| TS | Python |
|---|---|
name: string |
name: str |
age: number |
age: int / age: float |
flag: boolean |
flag: bool |
: string(返回值) |
-> str(返回值) |
`Hello ${name}` |
f"Hello {name}" |
注:这套注解在后面 FastAPI 篇会大量出现,那里类型注解会被「升级」成真正的请求参数校验。详见 FastAPI 相关章节。
3.3 默认参数
和 JS 一样支持默认值,但有一个致命坑(见第四节)。
// JavaScript
function createUser(name, role = "user") {
return { name, role }
}
3.4 关键字参数(Python 独有的爽点)
Python 调用函数时可以用参数名传参,顺序随意。这是 JS 没有的(JS 只能靠传一个对象模拟)。
// JavaScript:想要「具名传参」只能传对象
function connect({ host, port, timeout = 30 }) { /* ... */ }
connect({ port: 5432, host: "localhost" }) // 解构对象
这个特性让你读 SDK 调用时一眼看懂每个值的含义,例如:
四、args 和 kwargs:收集任意参数
这是本篇最该花时间的概念,也是读第三方库源码(到处是 def foo(*args, **kwargs))的前提。
4.1 args:收集多余的「位置参数」成元组
*args ≈ JS 的剩余参数 ...rest,把多传的位置参数打包成一个元组。
// JavaScript:剩余参数
function sum(...nums) { // nums 是数组
return nums.reduce((a, b) => a + b, 0)
}
sum(1, 2, 3) // 6
名字
args只是约定,关键是前面的*。*nums、*items都行。
4.2 kwargs:收集多余的「关键字参数」成字典
**kwargs 把所有「名字=值」形式的多余参数打包成一个字典。JS 没有直接对应物,最接近的是「把剩下的属性收进一个对象」。
// JavaScript:用对象 + 剩余属性近似
function request(url, { method = "GET", ...options } = {}) {
console.log(url, method, options) // options 收集其余配置
}
request("/api", { method: "POST", timeout: 30, retry: 3 })
4.3 两者组合 + 完整参数顺序
参数定义顺序是固定的,记成一句话:位置参数(可带默认值)→ *args → 关键字参数(可带默认值)→ **kwargs。注意 *args 之后定义的参数会变成「只能用关键字传」(keyword-only),比如下面综合示例里 *headers 后面的 timeout 就只能写成 timeout=60。最常见的组合是把 *args 和 **kwargs 放最后,表示「接收任意参数」:
4.4 反向操作: 和 用于「解包」
* / ** 出现在调用处时,作用相反——把列表/字典「拆开」成一个个参数。这正是 JS 展开运算符 ... 的两种用法合体。
// JavaScript:展开运算符
const nums = [1, 2, 3]
Math.max(...nums) // 数组展开成参数
const config = { host: "x", port: 1 }
connect({ ...config }) // 对象展开
记忆口诀:
- 定义函数时的
*args/**kwargs= 收集(多个值 → 一个集合) - 调用函数时的
*list/**dict= 解包(一个集合 → 多个值)
五、前端新手最易踩的坑
5.1 坑 1:默认参数不要用可变对象(list/dict)
JS 里默认值每次调用都重新求值,Python 默认值只在函数定义时求值一次,可变默认值会被多次调用「共享」,造成数据串台。
5.2 坑 2:缩进必须统一,别混用 Tab 和空格
Python 缩进是语法。同一个块里混用 Tab 和空格会报 TabError 或逻辑错乱。统一用 4 个空格(编辑器设置 Tab 转空格即可)。
5.3 坑 3:函数默认返回 None,不是 undefined
JS 函数不写 return 返回 undefined;Python 返回 None。判空时用 is None,别用 == None:
5.4 坑 4:别拿 lambda 当箭头函数写复杂逻辑
lambda 只能容纳一个表达式,不能写 if/for 语句块、不能多行。需要逻辑就老老实实 def。lambda 的正确用途是一次性小函数:
5.5 坑 5:range 是左闭右开,且不含 C 式 for
range(5) 是 0~4,range(2, 5) 是 2,3,4——和 JS for(i=0;i<5;i++) 的边界一致(右开),但别再想着写 i++。
六、综合示例:一个带各种参数的函数
七、总结
- 前端锚点:for 默认就是 for...of(遍历值,不是索引)。
- 控制流:先划清和 JS 的差异:这是 Python 第一眼最大的不同。
- 函数 def:重点章节:lambda 是 Python 的「箭头函数」,但只能放一个表达式,不能写多行逻辑——所以 Python 里写复杂逻辑一律用 def,lambda 只用于 sorted(key=...) 这类一次性小函数
- args 和 kwargs:收集任意参数:这是本篇最该花时间的概念,也是读第三方库源码(到处是 def foo(args, kwargs))的前提。
- 前端新手最易踩的坑:Python 缩进是语法。
- 没有花括号,缩进就是代码块:这是 Python 第一眼最大的不同。
学完自测
选择所有正确答案;提交后逐项核对判断依据。