Python 入门教程(面向有 Java 经验的开发者)
这不是一份"Python 知识点罗列",而是一份可以顺着学下去的教程。
目标:让已经有 Java 经验的开发者,在最短时间内建立 Python 的开发直觉,并能开始写脚本、做小工具、看懂常见 Python 项目代码。
这份教程怎么用
建议按顺序学习,不要跳着看。
每一章都尽量遵循同一种结构:
- 先讲这一章到底要学会什么
- 再给最小必要知识
- 再给 Java 对照,帮助你迁移已有经验
- 最后给练习,帮助你真正掌握
推荐节奏:
- 每次学 1 章
- 先自己照着敲代码,不要只看
- 每章末尾练习至少做 2 题
- 卡住时回看原始完整稿:
尚硅谷大模型技术之 Python(V3.0).md
学习路线总览
如果你有 Java 经验,第一轮只需要拿下这 7 件事:
- Python 的基本语法和缩进规则
- 常用数据类型与容器
- 条件、循环、函数
- 字符串、切片、推导式
- Python 风格的面向对象
- 模块、异常、文件操作
- 一些最常见的 Pythonic 写法
下面正式开始。
第 1 章 先跑起来:用 Python 写第一个程序
你要学会什么
学完这一章,你应该能:
- 安装并运行 Python 程序
- 理解 Python 文件如何执行
- 知道 Python 和 Java 在运行方式上的核心区别
1.1 先准备环境
建议使用 Python 3.11+。
查看版本:
bash
python3 -V
创建虚拟环境:
bash
python3 -m venv .venv
source .venv/bin/activate
安装包时尽量用:
bash
python -m pip install requests
而不是直接写 pip install,因为前者更明确地绑定当前解释器。
1.2 第一个 Python 程序
创建 hello.py:
python
print('hello python')
运行:
bash
python hello.py
1.3 先建立一个和 Java 不一样的认知
Java 中你通常会写:
java
public class Hello {
public static void main(String[] args) {
System.out.println("hello");
}
}
Python 不需要:
- 类包裹入口
main方法- 类型声明
- 分号结尾
Python 的基本哲学是:能更直接,就不要多写样板代码。
1.4 交互式解释器很好用
直接输入:
bash
python
然后可以立刻测试:
python
2 + 3
'hello'.upper()
len([1, 2, 3])
这对学习和试验非常有帮助。Java 开发者往往低估了这一点。
1.5 本章练习
- 创建
calc.py,打印3 * 7的结果。 - 进入 Python 交互模式,试一下:
10 / 3、10 // 3、10 % 3。 - 创建虚拟环境,并确认
python -V能正常输出版本。
第 2 章 语法入门:变量、类型、缩进
你要学会什么
学完这一章,你应该能:
- 定义变量
- 理解 Python 的几种基础类型
- 正确书写缩进和代码块
- 理解
None的意义 - 做最常见的类型转换和基础运算
2.1 变量不需要声明类型
python
name = 'Alice'
age = 18
height = 1.72
is_admin = False
和 Java 最大的区别是:
- Java:变量声明时写类型
- Python:变量不写类型,但对象本身有类型
python
print(type(name))
print(type(age))
2.2 最常见的基础类型
你第一轮最需要掌握这几个:
intfloatboolstrNone
示例:
python
count = 10
price = 19.9
ok = True
title = 'Python'
result = None
2.3 类型转换是日常开发高频操作
input() 读进来的内容默认是字符串,所以类型转换非常常见:
python
age = int('18')
price = float('19.9')
name = str(123)
flag = bool(1)
第一轮先记住这些最常用的:
int():转整数float():转浮点数str():转字符串bool():转布尔值
要注意:
python
print(bool(0))
print(bool(''))
print(bool('abc'))
2.4 先掌握最常用的运算符
算术运算:
python
print(10 + 3)
print(10 - 3)
print(10 * 3)
print(10 / 3)
print(10 // 3)
print(10 % 3)
print(2 ** 3)
比较与逻辑运算:
python
age = 20
print(age >= 18)
print(age < 60 and age >= 18)
print(age < 18 or age > 60)
print(not False)
对 Java 开发者来说,主要有两个容易注意的点:
- Python 用
and/or/not,不是&&/||/! ==比较值,is比较是否为同一个对象;入门阶段先把is主要用在x is None
2.5 None 不是"空字符串"
python
value = None
print(value)
None 更接近 Java 的 null,但要注意:
- 它是 Python 中一个明确存在的对象
- 常用来表示"没有值"或"还没结果"
比如:
python
def find_user(user_id):
return None
2.6 缩进就是语法
python
age = 20
if age >= 18:
print('成年人')
print('可以注册')
else:
print('未成年人')
这里没有花括号。if 下面缩进的代码就是这个代码块。
请严格记住:
- 统一用 4 个空格
- 不要混用 Tab 和空格
- 同一层级缩进必须一致
2.7 注释只需要先会一种
python
# 这是一行注释
够用了。别一开始把精力放在注释花样上。
2.8 Java 视角总结
这一章你只要完成 4 个观念切换:
- Python 不写变量类型,但对象有类型。
- Python 用缩进表达层级,不用
{}。 - 类型转换需要主动做,尤其是
input()之后。 - 逻辑运算写
and/or/not。
这几个点适应了,后面会顺很多。
2.9 本章练习
- 定义变量
name、age、salary,分别保存你的名字、年龄、薪资。 - 打印这 3 个变量的类型。
- 写一个
if判断:如果年龄大于等于 18,打印"成年人",否则打印"未成年人"。 - 让用户输入一个数字字符串,把它转成
int后加 10 再输出。
第 3 章 字符串与输入输出
你要学会什么
学完这一章,你应该能:
- 输出信息
- 获取用户输入
- 用
f-string拼接字符串 - 知道 Python 字符串是不可变对象
3.1 输出最常用的是 print
python
print('你好')
print(123)
print(True)
3.2 输入最常用的是 input
python
name = input('请输入你的名字: ')
print(name)
要特别注意:
input() 拿到的永远是字符串。
python
age = input('请输入年龄: ')
print(type(age))
如果要做数值计算,需要手动转换:
python
age = int(input('请输入年龄: '))
3.3 优先掌握 f-string
python
name = 'Alice'
age = 18
print(f'我叫{name},今年{age}岁')
这是 Python 中最常用、最推荐的字符串格式化方式。
3.4 字符串不可变
python
text = 'hello'
# text[0] = 'H' # 会报错
这点和 Java 的 String 很像:修改字符串,通常是得到一个新字符串。
3.5 常见字符串方法
python
text = ' hello python '
print(text.strip())
print(text.upper())
print(text.lower())
print(text.replace('python', 'java'))
print(text.split())
第一轮记住这些就够:
strip():去掉两端空白upper()/lower():大小写转换replace():替换split():拆分
3.6 本章练习
- 让用户输入姓名和年龄,然后打印:
你好,xxx,你今年 xx 岁。 - 输入一个带前后空格的字符串,打印去空格后的结果。
- 输入一个英文单词,打印它的大写形式。
第 4 章 容器入门:list、tuple、dict、set
你要学会什么
学完这一章,你应该能:
- 正确选择最常用的 4 类容器
- 对容器做增删改查
- 理解它们与 Java 容器的大致对应关系
- 掌握容器通用操作和成员判断
- 读懂后续 AI 代码里的
dict.items()、pop()、列表推导式
4.1 先建立总图
| Python | 含义 | Java 里大概像什么 |
|---|---|---|
list |
有序、可变序列 | ArrayList |
tuple |
有序、不可变序列 | 轻量只读数据 |
dict |
键值映射 | HashMap |
set |
无序不重复集合 | HashSet |
4.2 list:最常用的容器
python
nums = [10, 20, 30]
nums.append(40)
nums.insert(1, 15)
print(nums)
print(nums[0])
print(nums[-1])
删除:
python
nums.pop()
nums.remove(15)
print(nums)
和 Java 比,list 用起来通常更轻、更直接。
4.3 tuple:不可变序列
python
point = (100, 200)
print(point[0])
典型用途:
- 作为不可修改的数据
- 接收多返回值
- 解包
python
x, y = point
4.4 dict:Python 业务代码里的主力军
python
user = {
'name': 'Alice',
'age': 18,
}
print(user['name'])
print(user.get('email'))
user['age'] = 19
遍历:
python
for key, value in user.items():
print(key, value)
如果你以后写接口、配置、JSON 处理,dict 会出现得非常频繁。
4.5 set:去重与集合运算
python
tags = {'python', 'java', 'python'}
print(tags)
集合运算:
python
a = {1, 2, 3}
b = {3, 4, 5}
print(a | b)
print(a & b)
print(a - b)
4.6 容器通用操作也要熟
很多容器都支持一些通用能力:
python
nums = [10, 20, 30]
print(len(nums))
print(20 in nums)
print(max(nums))
print(min(nums))
print(sum(nums))
这几个函数非常高频:
len():长度in/not in:成员判断max()/min():最大最小值sum():求和,通常用于纯数字序列
字符串、列表、元组大多都能配合这些操作使用。
4.7 dict.items()、get()、pop() 后面会非常常见
python
batch = {'input_ids': [1, 2], 'labels': [1]}
print(batch.get('labels'))
for k, v in batch.items():
print(k, v)
labels = batch.pop('labels')
print(labels)
print(batch)
后面 AI 课程代码里会反复看到这种写法,比如:
for k, v in batch.items():遍历一批输入数据inputs.pop('labels'):把标签从输入字典里取出来word2index.get(token, unk_index):查词表,查不到就给默认值
你如果这里不熟,后面看训练代码会容易卡住。
4.8 切片和序列操作值得一起掌握
python
nums = [10, 20, 30, 40, 50]
print(nums[1:4])
print(nums[:3])
print(nums[::-1])
print(nums + [60, 70])
print('py' * 3)
这里你会顺手接触到两类常见操作:
- 切片:
sequence[start:end:step] - 序列拼接与重复:
+和*
4.9 什么时候该用哪个容器
一个简单判断:
- 要顺序、可修改:
list - 要键值对:
dict - 要去重:
set - 要只读小结构:
tuple
4.10 本章练习
- 定义一个列表,保存 5 个分数,打印第 1 个和最后 1 个分数。
- 定义一个字典,保存一个人的
name、age、city。 - 定义两个集合,输出它们的并集和交集。
- 用
for key, value in ...的方式遍历字典。 - 对一个数字列表分别输出长度、最大值、最小值和元素总和。
- 写一个小字典,使用
get()和pop()各操作一次。
第 5 章 流程控制:if、for、while
你要学会什么
学完这一章,你应该能:
- 写条件分支
- 使用
for遍历容器 - 使用
while处理重复逻辑 - 理解
break/continue - 理解 Python 中真值判断的习惯写法
5.1 if / elif / else
python
score = 85
if score >= 90:
print('A')
elif score >= 80:
print('B')
else:
print('C')
5.2 Python 更常用 for
python
for num in [1, 2, 3]:
print(num)
遍历数字范围:
python
for i in range(5):
print(i)
5.3 enumerate 很好用
python
names = ['Alice', 'Bob', 'Cathy']
for index, name in enumerate(names, start=1):
print(index, name)
当你既想拿到序号,又想拿到元素时,优先想到它。
5.4 while 的典型场景
python
count = 0
while count < 3:
print(count)
count += 1
如果只是遍历一个容器,通常 for 比 while 更自然。
5.5 break 和 continue 必须会
break:提前结束整个循环。
python
for num in [1, 2, 3, 4]:
if num == 3:
break
print(num)
continue:跳过本轮,进入下一轮。
python
for num in [1, 2, 3, 4]:
if num % 2 == 0:
continue
print(num)
5.6 Python 的真值判断很常见
python
items = []
if not items:
print('空列表')
这些值通常会被当成假:
None00.0''[]{}set()
这也是 Python 代码看起来更简洁的原因之一。
5.7 本章练习
- 输入一个分数,输出 A/B/C。
- 用
for打印 1 到 10。 - 用
while求 1 到 100 的和。 - 判断一个列表是否为空,并打印提示信息。
- 遍历 1 到 10,只打印奇数。
第 6 章 函数:把逻辑组织起来
你要学会什么
学完这一章,你应该能:
- 定义和调用函数
- 使用参数和返回值
- 理解默认参数、关键字参数、可变参数
- 理解作用域、递归、文档字符串
- 看懂参数解包
*args/**kwargs - 看懂后续 AI 代码里的
model(**inputs)、list[str]
6.1 函数定义
python
def add(a, b):
return a + b
result = add(3, 5)
print(result)
6.2 默认参数
python
def greet(name, msg='你好'):
print(f'{msg},{name}')
greet('Alice')
greet('Bob', '欢迎')
6.3 关键字参数
python
def create_user(name, age, city):
print(name, age, city)
create_user(name='Alice', age=18, city='Shanghai')
这会让调用代码更清晰。
6.4 可变参数
python
def total(*nums):
return sum(nums)
print(total(1, 2, 3, 4))
python
def show_user(**kwargs):
print(kwargs)
记忆方式:
*args收位置参数,得到tuple**kwargs收关键字参数,得到dict
6.5 参数解包调用,后面 AI 代码会直接用到
除了"定义函数时接收",* 和 ** 还可以在"调用函数时解包":
python
def add(a, b):
return a + b
nums = [3, 5]
print(add(*nums))
python
def show(name, age):
print(name, age)
user = {'name': 'Alice', 'age': 18}
show(**user)
后面 AI 课程里的 BERT 训练代码会直接出现:
python
outputs = model(**inputs)
意思就是:把 inputs 这个字典拆成 key=value 参数传给模型。
6.6 多返回值
python
def get_user():
return 'Alice', 18
name, age = get_user()
本质上返回的是一个元组。
6.7 类型注解建议尽早适应
python
def add(a: int, b: int) -> int:
return a + b
对 Java 开发者来说,这会让代码更熟悉一些,也更利于 IDE 提示。
你后面还会看到这种写法:
python
def tokenize(text) -> list[str]:
return list(text)
这里的 list[str] 表示"元素类型是 str 的列表"。后续 tokenizer 代码里就会出现这种注解。
6.8 作用域和 global 要有基本概念
python
count = 0
def increase():
global count
count += 1
increase()
print(count)
先记住这几个规则:
- 函数内部定义的变量,默认是局部变量
- 函数外部定义的变量,是全局变量
- 只有确实要在函数里修改全局变量时,才使用
global
实际开发里,global 不宜滥用。大多数时候,更推荐用参数和返回值传递数据。
6.9 递归先会看懂,再决定多用
python
def factorial(n: int) -> int:
if n == 1:
return 1
return n * factorial(n - 1)
递归的两个关键点:
- 递归终止条件
- 每次调用都更接近终止条件
第一轮学习时,会看懂、能写简单例子就够了。很多业务场景里,循环依然更直接。
6.10 文档字符串和函数说明
python
def add(a: int, b: int) -> int:
"""返回两个整数之和。"""
return a + b
这段三引号字符串就是文档字符串。它的作用是:
- 给别人说明函数是干什么的
- 让 IDE、帮助系统能直接显示说明
- 让代码更容易维护
6.11 pass 是占位关键字
python
def todo():
pass
当语法上需要先写一个函数体、类体,但你还没实现时,可以先用 pass 占位。后面 tokenizer 的基类方法里会见到它。
6.12 一个重要提醒
不要把 Python 函数写成 Java 工具类那种风格:
- 没必要所有函数都塞进一个
Utils类 - 没必要为了"面向对象"而强行包类
Python 中很多逻辑直接写函数就是最自然的。
6.13 本章练习
- 写一个函数,接收两个数字,返回较大值。
- 写一个函数,接收姓名和问候语,问候语默认为"你好"。
- 写一个函数,接收任意多个数字,返回它们的和。
- 写一个函数,返回姓名和年龄两个值,并在外部解包接收。
- 写一个递归函数,计算
n的阶乘。 - 写一个接收字典参数的函数,并用
**user的方式调用它。
第 7 章 切片、推导式、排序:开始有 Python 味道
你要学会什么
学完这一章,你应该能:
- 使用切片处理序列
- 使用列表推导式替代部分循环
- 用
sorted(..., key=...)处理常见排序问题 - 看懂条件表达式和简单
lambda - 知道
map/filter在 Python 里的基本位置
7.1 切片
python
nums = [10, 20, 30, 40, 50]
print(nums[1:4])
print(nums[:3])
print(nums[::2])
print(nums[::-1])
格式:
python
sequence[start:end:step]
7.2 列表推导式
python
nums = [1, 2, 3, 4]
squares = [n * n for n in nums]
evens = [n for n in nums if n % 2 == 0]
它本质上是在表达:
- 从一个序列取数据
- 做转换
- 或加条件过滤
这是 Python 非常重要的表达方式。
7.3 字典推导式和集合推导式
python
name_lengths = {name: len(name) for name in ['Alice', 'Bob']}
unique_lengths = {len(name) for name in ['Alice', 'Bob', 'Tom']}
7.4 排序时传 key
python
users = [
{'name': 'Alice', 'age': 20},
{'name': 'Bob', 'age': 18},
]
result = sorted(users, key=lambda user: user['age'])
print(result)
如果你用过 Java Stream,这个感觉会有点像"更轻量的转换和排序"。
7.5 条件表达式是 if-else 的简写
python
age = 20
label = '成年人' if age >= 18 else '未成年人'
print(label)
当逻辑很短、只是做二选一赋值时,这种写法很常见。
7.6 lambda 先学会看懂
lambda 就是匿名函数,常见于"只用一次的小函数":
python
add = lambda a, b: a + b
print(add(2, 3))
但更常见的场景其实是配合排序、映射、过滤:
python
nums = [1, 2, 3, 4]
print(list(map(lambda x: x * 2, nums)))
print(list(filter(lambda x: x % 2 == 0, nums)))
学习建议:
- 先会看懂
lambda - 简单场景下更优先考虑列表推导式,因为通常更直观
7.7 本章练习
- 把一个列表倒序输出。
- 用列表推导式生成 1 到 10 的平方列表。
- 从列表中筛选所有偶数。
- 对一组字典按
age排序。 - 用条件表达式判断一个数字是奇数还是偶数。
第 8 章 面向对象:保留 Java 开发者真正需要的部分
你要学会什么
学完这一章,你应该能:
- 定义类和实例
- 理解
self、__init__ - 使用继承和方法重写
- 看懂
super()和常见特殊方法 - 理解 Python 的鸭子类型与 Java 接口思维的不同
- 看懂后续 AI 代码里的
nn.Module、Dataset、Tokenizer 类设计
8.1 定义类
python
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def speak(self, msg: str) -> None:
print(f'{self.name}: {msg}')
使用:
python
p = Person('Alice', 18)
p.speak('你好')
8.2 self 怎么理解
可以把 self 理解成 Java 里的 this。
区别在于:
- Java 里的
this通常不写在参数列表里 - Python 必须显式写出来
8.3 类属性、类方法、静态方法
python
class User:
total = 0
def __init__(self, name: str):
self.name = name
User.total += 1
@classmethod
def count(cls) -> int:
return cls.total
@staticmethod
def is_valid_name(name: str) -> bool:
return len(name.strip()) > 0
你只需要先记住:
- 实例方法处理对象自身状态
- 类方法通常处理类级别状态或工厂逻辑
- 静态方法只是"放在类里的普通函数"
后面 AI 课程的 tokenizer 里会大量出现 @classmethod / @staticmethod,比如 from_vocab()、tokenize()。
8.4 继承、重写、super() 都会在 AI 代码里出现
python
class Animal:
def speak(self) -> None:
print('...')
class Dog(Animal):
def __init__(self, name):
super().__init__()
self.name = name
def speak(self) -> None:
print('wang')
在后续 AI 代码里,你会看到很多类这样写:
class ReviewAnalyzeModel(nn.Module)class TranslationDataset(Dataset)super().__init__()
这说明它们是在继承框架提供的父类能力。
8.5 特殊方法 __len__、__getitem__ 要认识
python
class MyList:
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __getitem__(self, index):
return self.data[index]
这样你就可以写:
python
obj = MyList([10, 20, 30])
print(len(obj))
print(obj[0])
后面的自定义数据集代码会直接用这两个方法配合 DataLoader 工作,所以这不是"只会在语法书里出现的知识点"。
8.6 鸭子类型比"接口约束"更常见
python
def make_speak(obj):
obj.speak()
只要传入对象能调用 speak(),这段代码就可以工作。
这就是典型的 Python 风格:
- 不那么强调"你是什么类型"
- 更强调"你能不能完成这个行为"
8.7 第一轮不必深挖的 OOP 主题
先不用急着深挖这些:
- 魔法方法的完整体系
- 抽象类
- 多重继承细节
- 元类
先把"类怎么写、对象怎么用、继承怎么读懂"掌握住更重要。
8.8 本章练习
- 写一个
Student类,包含name和score。 - 给
Student类加一个show()方法,打印学生信息。 - 写一个
Animal和Dog,让Dog重写speak()。 - 写一个函数,接收任意有
speak()方法的对象并调用它。 - 写一个类,实现
__len__和__getitem__。
第 9 章 模块与包:开始像写项目一样组织代码
你要学会什么
学完这一章,你应该能:
- 理解模块和包
- 使用几种常见导入方式
- 知道
__name__ == '__main__'的作用 - 理解第三方包和虚拟环境的关系
- 先知道几个常见标准库
- 把代码从"一个文件"整理成"小项目结构"
9.1 一个 .py 文件通常就是一个模块
math 是模块,你自己的 user_service.py 也是模块。
python
import math
print(math.sqrt(16))
9.2 常见导入方式
python
import math
import math as m
from math import sqrt
建议:
- 日常优先
import module - 或
from package import module - 少用
from xxx import *
9.3 __name__ == '__main__' 是什么
python
def main():
print('程序启动')
if __name__ == '__main__':
main()
它的意思是:
- 当前文件被"直接运行"时,执行这段代码
- 当前文件被"当作模块导入"时,不执行这段代码
这在写可复用模块时非常常见。
9.4 一个简单项目结构
text
project/
├── app/
│ ├── __init__.py
│ ├── service.py
│ └── utils.py
├── main.py
└── requirements.txt
__init__.py 的作用,入门阶段先理解成:告诉 Python"这个目录要作为包来使用"。
9.5 第三方包和虚拟环境
bash
python -m pip install requests
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt
虚拟环境的核心意义是:
- 每个项目有自己的依赖集合
- 避免全局环境互相污染
- 更容易和别人同步项目环境
9.6 常见标准库先知道名字
第一轮不需要全背,但至少知道这些高频模块:
pathlib:路径处理json:JSON 编解码datetime:日期时间collections:增强型容器itertools:迭代工具functools:函数式工具os/sys:系统交互
以后你写脚本、做文件处理、调接口时,很快就会碰到它们。
9.7 Java 迁移提示
你可以把它粗略理解为:
- 模块接近"单个源码文件里的可复用功能"
- 包接近"目录级组织结构"
但 Python 的导入方式通常比 Java 轻很多。
9.8 本章练习
- 创建
math_utils.py,写一个加法函数。 - 在另一个文件中导入并调用这个函数。
- 尝试给模块起别名导入。
- 写一个带
main()和if __name__ == '__main__':的脚本。
第 10 章 异常处理:让程序更稳
你要学会什么
学完这一章,你应该能:
- 使用
try/except处理异常 - 捕获特定类型异常
- 主动抛出异常
- 理解异常传递
- 在需要时定义自定义异常类
10.1 基本写法
python
try:
num = int(input('请输入数字: '))
print(10 / num)
except ValueError:
print('输入的不是整数')
except ZeroDivisionError:
print('除数不能为 0')
else:
print('执行成功')
finally:
print('一定会执行')
10.2 主动抛出异常
python
def register(age: int) -> None:
if age < 0:
raise ValueError('age 不能小于 0')
10.3 异常会向上层传递
python
def divide(a, b):
return a / b
def calculate():
return divide(10, 0)
try:
calculate()
except ZeroDivisionError:
print('捕获到除零错误')
这说明:底层函数没处理的异常,会一层层往上抛,直到被某一层捕获。
10.4 自定义异常类什么时候用
python
class AgeError(Exception):
pass
def register(age: int) -> None:
if age < 0:
raise AgeError('年龄不能为负数')
当你希望错误语义更清晰、更贴近业务时,就可以定义自己的异常类。
10.5 3 个很实用的建议
- 只捕获你能处理的异常
- 不要无脑写
except Exception: - 错误信息尽量能帮助定位问题
10.6 本章练习
- 输入一个数字并输出
100 / num,处理输入错误和除零错误。 - 写一个函数,当年龄小于 0 时抛出异常。
- 试着分别触发
ValueError和ZeroDivisionError。 - 自定义一个异常类,用来表示"用户名不能为空"。
第 11 章 文件操作:这是脚本开发的高频能力
你要学会什么
学完这一章,你应该能:
- 读取文本文件
- 写入文本文件
- 理解为什么
with是最佳实践 - 掌握常见文件模式
- 理解绝对路径和相对路径
- 初步使用
pathlib做路径和目录操作 - 看懂后续 AI 代码里的
Path(__file__)和with torch.no_grad()
11.1 读取文件
python
with open('data.txt', 'rt', encoding='utf-8') as f:
content = f.read()
print(content)
11.2 写入文件
python
with open('output.txt', 'wt', encoding='utf-8') as f:
f.write('hello\n')
11.3 常见文件模式先记住这些
rt:读文本wt:写文本,会覆盖原内容at:追加文本,在文件末尾继续写rb/wb:读写二进制文件
追加写入示例:
python
with open('log.txt', 'at', encoding='utf-8') as f:
f.write('new line\n')
11.4 绝对路径 vs 相对路径
- 绝对路径:从磁盘根位置开始写的完整路径
- 相对路径:相对于当前工作目录的路径
python
from pathlib import Path
print(Path('data.txt').resolve())
入门阶段的建议是:
- 小练习先用相对路径
- 真正做项目时,尽量明确路径基准,避免"在不同目录运行就找不到文件"
11.5 pathlib 和 __file__ 在项目里非常常见
python
from pathlib import Path
ROOT_DIR = Path(__file__).parent
DATA_DIR = ROOT_DIR / 'data'
这里有两个重点:
__file__:当前 Python 文件自己的路径Path(... ) / 'child':pathlib提供的路径拼接写法
后面 AI 课程里的 config.py 基本就是这样组织项目目录的,所以你最好在这里就先熟悉。
11.6 为什么总推荐 with
因为它和 Java 的 try-with-resources 很像:
- 更安全
- 自动释放资源
- 异常时也能正确关闭文件
而且 with 不只用于文件。后面推理代码里你还会看到:
python
with torch.no_grad():
pass
这里的意思是:在这个代码块里关闭梯度计算,做推理更省资源。
11.7 路径建议优先用 pathlib
python
from pathlib import Path
path = Path('logs/app.log')
print(path.exists())
print(path.parent)
11.8 目录操作也很常用
python
from pathlib import Path
base = Path('data')
base.mkdir(exist_ok=True)
for item in base.iterdir():
print(item)
入门阶段先会这几个就很够用:
Path.exists():判断路径是否存在Path.mkdir():创建目录Path.iterdir():遍历目录内容Path.is_file()/Path.is_dir():判断文件或目录
11.9 本章练习
- 读取一个文本文件,并打印内容。
- 把一段字符串写入另一个文件。
- 使用
Path判断某个文件是否存在。 - 创建一个目录,并遍历这个目录下的内容。
- 用
Path(__file__)的思路,写一个脚本打印当前文件所在目录。
第 12 章 一些现在就该养成的 Python 习惯
12.1 能直接遍历元素,就不要手搓下标
不推荐:
python
names = ['Alice', 'Bob']
for i in range(len(names)):
print(names[i])
更推荐:
python
for name in names:
print(name)
如果确实要索引,用 enumerate()。
12.2 能写推导式时,不要总写冗长循环
不推荐:
python
result = []
for n in [1, 2, 3, 4]:
result.append(n * n)
更推荐:
python
result = [n * n for n in [1, 2, 3, 4]]
12.3 能写函数,就不要急着写工具类
很多 Java 开发者的惯性是:
- 先建类
- 再放静态方法
但 Python 中很多场景直接写模块级函数更自然。
12.4 优先写清晰,而不是写炫技代码
短不是目的,清楚才是目的。
12.5 可变对象、浅拷贝、深拷贝值得尽早知道
python
import copy
nums1 = [[1, 2], [3, 4]]
nums2 = copy.copy(nums1)
nums3 = copy.deepcopy(nums1)
可以先这样理解:
- 直接赋值:只是多个变量指向同一个对象
- 浅拷贝:只复制最外层,里面嵌套的对象可能还是共用的
- 深拷贝:连里面嵌套的对象也一起复制
这在处理嵌套列表、字典时很容易踩坑,所以提前知道很有价值。
12.6 第一轮别急着钻高级特性
这些知识点不是不重要,而是更适合放在"已经会写普通业务代码之后"再系统学。先知道它们是什么、解决什么问题,就够了。
- 装饰器:本质上是"接收函数并返回新函数"的语法糖,常用于日志、鉴权、缓存、路由注册等横切逻辑。你以后在 Flask、FastAPI、pytest 里会经常见到
@xxx。 - 闭包:指内部函数记住并继续使用外部函数作用域中的变量。它常用于"带状态的函数"、延迟执行、工厂函数。你可以把它理解成:不用类,也能把少量状态和行为绑定在一起。
- 迭代器内部机制:核心是
iter()和next(),以及"一个一个地产出数据"。平时你写for循环时就在使用它,只是 Python 帮你隐藏了细节。理解它以后,才更容易真正看懂生成器、惰性计算和很多标准库工具。 - 生成器:可以把它看成"会按需产生数据的函数/对象",比一次性生成整份列表更省内存。以后你看到
yield时,就知道它和普通return不一样。 - 协程:主要用于高并发 IO 场景,比如同时发很多网络请求、下载很多网页、处理大量异步任务。它不是"更快的线程",而是一种在单线程中高效切换等待任务的方式,典型写法会看到
async/await。 - 多进程多线程:用于并发执行任务。一般可以粗略记住:IO 密集型任务先考虑线程或协程,CPU 密集型任务更常考虑多进程。等你真正遇到性能问题、批量处理任务、爬虫、并发下载时再深入,会更容易建立直觉。
更好的学习顺序是:
- 先把函数、容器、模块、异常、文件操作写熟。
- 再写几个真实脚本,体会"代码组织"和"数据流动"。
- 然后回头学装饰器、闭包、迭代器、生成器。
- 最后再进并发:线程、进程、协程。
这样再学这些高级主题,你不会只记住名词,而是能理解它们到底在解决什么问题。
第 13 章 面向 AI 代码的前置语法补充
这一章是专门按你后面要学的 AI 代码反推出来的。
目标不是继续扩大知识点范围,而是提前把"你以后一定会反复见到的 Python 写法"单独讲清楚,这样你后面看训练代码、推理代码、Tokenizer、Dataset、Model 代码时,不会卡在语法上。
13.1 先把后面的 AI 项目代码拆成 5 类文件来看
你后面看到的大多数项目,代码结构其实都很像:
config.py:放路径、超参数、常量dataset.py:准备数据、定义Dataset/DataLoadertokenizer.py:分词、编码、解码、词表model.py:定义模型类train.py/predict.py/evaluate.py:训练、推理、评估流程
也就是说,后面你要面对的不是"完全陌生的 Python",而是这些基础语法在固定模式里重复出现。
13.2 元组解包:后面会频繁出现在 dataloader 循环里
python
pair = ('Alice', 18)
name, age = pair
print(name, age)
在循环里也一样:
python
for inputs, targets in dataloader:
print(inputs)
print(targets)
后面会怎么用到:
for inputs, targets in dataloader:for input_tensor, target_tensor in train_dataloader:
如果你理解成"每一轮循环都会拿到一个二元组,然后自动拆开",就不会卡住。
13.3 多维切片:这是看张量代码最容易卡住的点之一
你前面已经学过切片,但后面的 AI 代码会把它写到二维、三维数据上:
python
targets = data[:, 1:]
last = output[:, -1, :]
first_col = hidden[:, 0, :]
先只按 Python 语法理解它:
- 第一个
:表示第 1 维全取 1:表示从下标 1 取到末尾-1表示最后一个位置- 最后一个
:表示第 3 维全取
后面会怎么用到:
targets[:, :-1]:去掉最后一个 token,作为 decoder 输入targets[:, 1:]:去掉第一个 token,作为训练目标output[:, -1, :]:取每个样本最后一步的输出last_hidden_state[:, 0, :]:取 BERT 的 CLS 位置向量
你现在先把它当作"多维列表切片语法"看,就能先读懂大部分模型代码。
13.4 方法链和对象调用:不要被点号吓到
后面 AI 代码会有大量这种写法:
python
x = x.to(device)
model = model.to(device)
value = tensor.item()
result = tensor.tolist()
这本质上只是"对象调用自己的方法":
obj.method()- 有时返回新对象
- 有时返回普通值
后面会怎么用到:
.to(device):把张量或模型放到 CPU/GPU.train()/.eval():切换训练/推理模式.item():把单个数值张量变成普通 Python 数字.tolist():把张量转成 Python 列表.reshape()/.squeeze()/.unsqueeze():调整张量形状
所以你后面看到这种代码时,先不要急着想"深度学习数学含义",先按"Python 对象方法调用"去读。
13.5 squeeze、unsqueeze、reshape:先把名字和形状变化读懂
python
x = x.unsqueeze(1)
x = x.squeeze(-1)
x = x.reshape(-1)
你现在只需要先掌握直觉:
unsqueeze(dim):增加一个长度为 1 的维度squeeze(dim):压掉一个长度为 1 的维度reshape(...):换一种形状看同一份数据
后面会怎么用到:
context_vector.unsqueeze(0):给隐藏状态补一个维度output.squeeze(-1):把最后一个长度为 1 的维度压掉decoder_targets.reshape(-1):拉平成一维,方便算 loss
这部分虽然是张量操作,但卡人的第一步往往不是数学,而是没读懂 Python 调用长什么样。
13.6 字典推导式 + 批量搬运:训练代码里特别常见
python
inputs = {'a': 1, 'b': 2}
result = {k: v * 10 for k, v in inputs.items()}
print(result)
后面会怎么用到:
python
inputs = {k: v.to(device) for k, v in batch.items()}
这句可以拆成三层意思:
- 遍历字典里的每组
key, value - 对每个
value调用.to(device) - 再组装成一个新字典
如果你能把这种推导式看顺,后面的训练代码阅读难度会下降很多。
13.7 model(**inputs):这是"字典解包调用",不是黑魔法
python
def show(name, age):
print(name, age)
user = {'name': 'Alice', 'age': 18}
show(**user)
这和后面的:
python
outputs = model(**inputs)
是同一件事。
意思就是:
inputs是一个字典- 把字典里的每个键值对拆开
- 按关键字参数的方式传给函数或模型
后面会怎么用到:
- BERT 模型输入字典里会有
input_ids、attention_mask、token_type_ids - 用
model(**inputs)可以直接传进去,不用手写每个参数
13.8 if __name__ == '__main__': 在后续项目里几乎是标配
python
def train():
print('start')
if __name__ == '__main__':
train()
后面会怎么用到:
train.py直接运行时开始训练predict.py直接运行时进入推理交互dataset.py/tokenizer.py直接运行时做小测试
你看到这句时,脑子里只要翻译成:
- "这个文件如果是直接运行,就执行下面这段"
就够了。
13.9 while True + break + continue:交互式推理脚本的固定模板
python
while True:
user_input = input('> ')
if user_input == 'q':
break
if user_input.strip() == '':
continue
print(user_input)
后面会怎么用到:
- 命令行情感分析
- 中英翻译 demo
- 交互式输入测试
看到这种代码时,你就按这个模板理解:
- 一直循环
- 输入
q退出 - 空输入跳过
- 否则正常处理
13.10 Path(__file__) + config.py:后面项目组织的核心套路
python
from pathlib import Path
ROOT_DIR = Path(__file__).parent
DATA_DIR = ROOT_DIR / 'data'
后面会怎么用到:
- 定义
RAW_DATA_DIR - 定义
PROCESSED_DATA_DIR - 定义
MODELS_DIR - 定义
LOGS_DIR
这类代码的价值是:
- 让项目路径不依赖你当前命令行在哪个目录
- 让训练、预测、评估脚本都共享一套路径配置
所以以后你看到 config.py,不要把它当成陌生写法,它本质上就是"项目常量集中管理"。
13.11 @classmethod / @staticmethod:后面 tokenizer 里会反复出现
python
class Demo:
@staticmethod
def clean(text):
return text.strip()
@classmethod
def from_file(cls, path):
return cls()
后面会怎么用到:
from_vocab():从词表文件创建 tokenizerbuild_vocab():通过类方法构建词表tokenize():有时会写成静态方法或类方法
简单记法:
@staticmethod:和类放在一起,但不依赖实例状态@classmethod:会拿到cls,常用来做"替代构造器"
13.12 __len__ / __getitem__:自定义数据集的关键入口
python
class DemoDataset:
def __len__(self):
return 100
def __getitem__(self, index):
return index
后面会怎么用到:
class ReviewAnalyzeDataset(Dataset)class TranslationDataset(Dataset)
当你看到这两个方法时,先理解成:
__len__:告诉外界"我有多少条数据"__getitem__:告诉外界"怎么按下标取一条数据"
这样就能读懂为什么 DataLoader 可以去遍历它。
13.13 你后面最需要先会"读懂"的,不是所有 PyTorch,而是这几个固定模式
你在后续 AI 学习里,最常先碰到的代码形态其实就这些:
device = torch.device('cuda' if ... else 'cpu')model = Model(...).to(device)for batch in dataloader:inputs = {k: v.to(device) for k, v in batch.items()}outputs = model(**inputs)loss.backward()/optimizer.step()/optimizer.zero_grad()model.eval()+with torch.no_grad():
你现在不用把它们全学成"框架原理",但至少要做到:
- Python 语法能读顺
- 看见这些模式时不慌
- 能分辨"这是 Python 写法"还是"这是 PyTorch API"
13.14 这一章学完后,你的目标不是会写模型,而是做到"不在 Python 语法上掉队"
如果你能做到下面这些,后续 AI 学习曲线就会顺很多:
- 看到
for inputs, targets in dataloader不慌 - 看到
targets[:, :-1]能知道这是切片 - 看到
model(**inputs)能知道这是字典解包调用 - 看到
Path(__file__)能知道这是项目路径定位 - 看到
super().__init__()、__len__、__getitem__能知道这是类的标准写法 - 看到
with torch.no_grad():能先知道它是with语法,不会先卡死在形式上
13.15 本章练习
- 写一个
while True的小交互脚本,输入q退出,空字符串跳过。 - 写一个字典推导式,把
{'a': 1, 'b': 2}变成{'a': 10, 'b': 20}。 - 写一个函数,定义参数
name, age,然后用**user的方式调用它。 - 写一个类,实现
__len__和__getitem__。 - 写一段切片代码,分别演示
[:, :-1]、[:, 1:]、[:, -1]这三种形式在"二维列表语义"里的含义。
第 14 章 PyTorch 最小必备:先做到能读懂训练代码
这一章不是要把 PyTorch 讲成框架手册,而是按你后面课程代码里最常出现的模式来讲。
目标非常明确:
- 先能看懂
train.py、predict.py、model.py - 先知道一段 PyTorch 代码到底在"准备数据 / 前向传播 / 算损失 / 反向传播 / 保存模型"的哪一步
- 先不在
tensor、shape、device、loss这些词上卡住
14.1 Tensor 可以先理解成"带更多能力的多维数组"
python
import torch
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float)
print(x)
print(x.shape)
你现在先这样理解就够了:
- 标量:一个数
- 向量:一维数组
- 矩阵:二维数组
- Tensor:更一般化的多维数据容器
后面会怎么用到:
- 文本 token id 会被放进 tensor
- 一批样本会组成二维或三维 tensor
- 模型输入输出基本都是 tensor
14.2 先把 shape 当成"数据的外形说明书"
python
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x.shape) # torch.Size([2, 3])
这表示:
- 有 2 行
- 每行 3 个元素
后面 AI 代码里你会反复看到这些名字:
batch_size:一批有多少样本seq_len:每个样本有多少个 tokenvocab_size:词表大小hidden_size:隐藏层维度embedding_dim/dim_model:向量维度
比如:
[batch_size, seq_len][batch_size, seq_len, hidden_size][batch_size, vocab_size]
后面会怎么用到:
review-analyze-bert/src/model.py里会看到last_hidden_state[:, 0, :]translation-seq2seq/src/train.py里会看到decoder_outputs.reshape(-1, ...)translation-transformer/src/model.py里会看到[batch_size, seq_len, dim_model]
14.3 dtype 和 device:一个管"数据类型",一个管"放哪算"
python
x = torch.tensor([1, 2, 3], dtype=torch.long)
print(x.dtype)
常见 dtype:
torch.long:整数 id,常用于 token 索引torch.float:浮点数,常用于特征、loss、概率
device 表示数据放在哪个设备上:
python
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
后面会怎么用到:
- token id tensor 通常用
long - 标签有时会转成
float,比如二分类的BCEWithLogitsLoss - 模型和数据都要
.to(device),否则设备不一致会报错
14.4 Dataset、DataLoader、batch:训练代码的"数据入口三件套"
python
from torch.utils.data import Dataset, DataLoader
class DemoDataset(Dataset):
def __len__(self):
return 100
def __getitem__(self, index):
return index, index + 1
dataloader = DataLoader(DemoDataset(), batch_size=4, shuffle=True)
可以这样理解:
Dataset:告诉 PyTorch"单条数据怎么取"DataLoader:负责按 batch 取数据、打乱、迭代batch_size:每次送给模型多少条样本
后面会怎么用到:
review-analyze-rnn/src/dataset.pytranslation-transformer/src/dataset.pyfor inputs, targets in dataloader:
14.5 collate_fn 和 pad_sequence:为变长序列凑成一个 batch
python
from torch.nn.utils.rnn import pad_sequence
为什么需要它:
- 文本长度不一样
- 一个 batch 里的句子必须整理成同样长度,才能堆叠起来
后面会怎么用到:
translation-seq2seq/src/dataset.pytranslation-transformer/src/dataset.py
如果你后面看到:
collate_fn=batch_fnpad_sequence(..., batch_first=True, padding_value=0)
就先理解成:
- "把不同长度的句子补齐后再组成 batch"
14.6 nn.Module:PyTorch 里模型类的标准父类
python
from torch import nn
class DemoModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 2)
def forward(self, x):
return self.linear(x)
你只要先记住:
- 继承
nn.Module - 在
__init__里定义层 - 在
forward()里定义数据如何流动
后面会怎么用到:
class ReviewAnalyzeModel(nn.Module)class TranslationModel(nn.Module)- 你调用
model(inputs)时,本质上是在触发forward()
14.7 先认识后面最常见的层
最重要的不是背参数,而是先知道它们大概干什么。
nn.Embedding:把 token id 映射成向量(后面在 RNN、GRU、LSTM、Transformer 里都会用)nn.Linear:线性层,常用于输出分类结果或维度变换nn.RNN/nn.GRU/nn.LSTM:处理序列数据nn.Transformer:Transformer 主体
后面会怎么用到:
input-method-rnn/src/model.py:nn.Embedding+nn.RNNreview-analyze-gru/src/model.py:nn.GRUreview-analyze-lstm/src/model.py:nn.LSTMtranslation-transformer/src/model.py:nn.Transformerreview-analyze-bert/src/model.py:AutoModel+nn.Linear
14.8 训练循环其实就 6 步
后面的训练代码不管换什么模型,主骨架都很像:
python
model.train()
for batch in dataloader:
outputs = model(...)
loss = loss_fn(outputs, targets)
loss.backward()
optimizer.step()
optimizer.zero_grad()
你可以把它背成固定模板:
- 切到训练模式
model.train() - 取一批数据
- 做前向传播
model(...) - 算损失
loss_fn(...) - 反向传播
loss.backward() - 更新参数
optimizer.step(),再清梯度optimizer.zero_grad()
后面会怎么用到:
- 几乎所有
src/train.py
14.9 loss.backward()、optimizer.step()、zero_grad() 分别在做什么
先用最直白的话理解:
loss.backward():根据 loss 反向计算梯度optimizer.step():根据梯度更新参数optimizer.zero_grad():把上一次梯度清掉,避免累加污染下一轮
你现在不需要先理解求导公式,但一定要先知道这三句各自负责什么。
14.10 train() 和 eval():同一个模型有两种工作状态
python
model.train()
model.eval()
先记住:
- 训练时用
train() - 推理/评估时用
eval()
后面会怎么用到:
train.py中:model.train()predict.py、evaluate.py中:model.eval()
14.11 with torch.no_grad()::推理时关闭梯度
python
model.eval()
with torch.no_grad():
outputs = model(x)
它的作用是:
- 推理时不记录梯度
- 更省显存/内存
- 更快一些
后面会怎么用到:
- 所有
predict.py - 部分
evaluate.py
14.12 logits、概率、预测类别:别混在一起
这是后面最容易混淆的概念之一。
logits:模型直接输出的原始分数- 概率:对 logits 做激活后得到
- 预测类别:从概率或 logits 里挑最终结果
常见对应关系:
- 二分类:常看到
torch.sigmoid(output) - 多分类:常看到
torch.softmax(logits, dim=-1)或直接torch.argmax(...)
后面会怎么用到:
review-analyze-bert/src/predict.py:torch.sigmoid(output)translation-seq2seq/src/predict.py:torch.argmax(decoder_output, dim=-1)translation-attention/src/model.py:torch.softmax(attention_scores, dim=-1)
14.13 两种常见 loss,要先能区分场景
后续代码里最常见的就是这两个:
BCEWithLogitsLoss:二分类,输入通常是 logits,标签常是 0/1CrossEntropyLoss:多分类或序列分类,目标通常是类别索引
后面会怎么用到:
- 情感分析:
BCEWithLogitsLoss - 翻译、输入法预测:
CrossEntropyLoss
看到 loss 名字时,你先不用深挖公式,先判断"这是二分类还是多分类/序列预测"。
14.14 ignore_index:告诉 loss 忽略 padding 位置
python
loss_fn = torch.nn.CrossEntropyLoss(ignore_index=pad_token_index)
后面会怎么用到:
- 翻译任务里,batch 中很多位置只是补齐用的 pad
- 这些位置不应该参与 loss
所以 ignore_index 的直觉是:
- "这些位置是凑形状的,不是真实训练目标"
14.15 保存与加载模型:先记住 state_dict
python
torch.save(model.state_dict(), 'best.pt')
model.load_state_dict(torch.load('best.pt'))
你可以先把 state_dict 理解成:
- 模型参数的字典快照
后面会怎么用到:
- 训练时保存最优模型
- 预测时重新加载模型参数
14.16 TensorBoard / SummaryWriter:记录训练过程
python
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(log_dir='logs')
writer.add_scalar('Loss', 0.5, 1)
writer.close()
后面会怎么用到:
writer.add_scalar('Loss', loss, epoch)
你只要先知道:
- 它是拿来记录训练曲线的
- 最常见记录项就是 loss
14.17 看后续 PyTorch 代码时的阅读顺序
建议你按这个顺序看:
- 先看
config.py,知道路径和超参数 - 再看
dataset.py,知道一条样本长什么样 - 再看
model.py,知道输入输出 shape 变化 - 再看
train.py,理解训练流程 - 最后看
predict.py/evaluate.py
这样比一上来啃 model.py 顺很多。
14.18 本章练习
- 用
torch.tensor()创建一个二维 tensor,并打印它的 shape。 - 写一个最小的
nn.Module类,里面只放一个Linear层。 - 写出一个伪训练循环,至少包含
model.train()、loss.backward()、optimizer.step()、optimizer.zero_grad()。 - 写一段推理代码,包含
model.eval()和with torch.no_grad():。 - 用一句话解释
BCEWithLogitsLoss和CrossEntropyLoss各更适合什么任务。
第 15 章 面向 AI 项目的最小知识地图:Tokenizer、任务、训练流程
这一章不讲深理论,只讲你后面看 NLP / AI 项目代码时最先需要的概念地图。
目标是:
- 知道这些 AI 术语到底在代码里落到哪里
- 知道一份 NLP 项目代码大概在解决什么问题
- 不会因为概念名词太多而看不懂项目结构
15.1 文本进入模型前,通常先经过这条流水线
最常见的流程是:
- 原始文本
- tokenizer 分词/切词
- token 转 id
- 补齐或截断长度
- 组成 batch
- 喂给模型
- 模型输出 logits / 概率 / 类别
后面会怎么用到:
process.pytokenizer.pypredict.py
15.2 tokenizer 到底在做什么
可以先把 tokenizer 理解成:
- 把"人类文本"变成"模型能处理的 token 序列"
常见动作:
tokenize():切词encode():转成 iddecode():把 id 还原成文本
后面会怎么用到:
JiebaTokenizerChineseTokenizerEnglishTokenizerAutoTokenizer.from_pretrained(...)
15.3 词表 vocab 和几个特殊 token 必须认识
后面你会经常看到:
unk_token:未知词pad_token:补齐占位sos_token:序列开始eos_token:序列结束
这些不是"语法点",但如果不认识,后面翻译代码很容易看不懂。
后面会怎么用到:
input-method-rnn/src/tokenizer.pytranslation-transformer/src/tokenizer.py
15.4 为什么要 padding、truncation、max_length
因为文本长度不一样,而模型往往要求 batch 里的形状整齐。
常见写法:
python
tokenizer(text, padding='max_length', truncation=True, max_length=128)
意思是:
- 太短就补齐
- 太长就截断
- 最终都整理到同一长度
后面会怎么用到:
review-analyze-bert/src/process.pyreview-analyze-bert/src/predict.py- Hugging Face notebook 里的 tokenizer 示例
15.5 attention_mask、token_type_ids 可以先知道用途,不必先深挖原理
你后面在 BERT 输入里会看到这些字段:
input_idsattention_masktoken_type_ids
先这样理解就够:
input_ids:真正的 token idattention_mask:哪些位置是有效内容,哪些位置是 paddingtoken_type_ids:句子对任务中区分句子 A / B
后面会怎么用到:
review-analyze-bert/src/process.pyreview-analyze-bert/src/train.pyreview-analyze-bert/src/predict.py
15.6 return_tensors='pt' 的意思很简单
python
inputs = tokenizer(text, return_tensors='pt')
意思就是:
- 直接把 tokenizer 的输出整理成 PyTorch tensor 格式
这里的 'pt' 就是 PyTorch 的缩写。
15.7 数据集划分:train / test / valid 本质上在分工
最基本的理解:
train:训练用test:最后评估用valid/dev:调参、挑模型用
后面会怎么用到:
train_test_split(...)dataset.train_test_split(...)
你先不用纠结命名差异,只要知道"不同数据集承担不同职责"。
15.8 你后面会遇到两类典型任务
第一类:文本分类
输入:一段文本
输出:一个类别或一个分数
后面会怎么用到:
- 情感分析 RNN/GRU/LSTM/BERT 项目
代码直觉通常是:
- 输入 shape 常是
[batch_size, seq_len] - 输出 shape 常是
[batch_size]或[batch_size, num_classes]
第二类:序列到序列任务(Seq2Seq)
输入:一个序列
输出:另一个序列
后面会怎么用到:
- 中英翻译项目
- 输入法预测的部分思路
代码直觉通常是:
- 输入、输出都不是单个类别,而是一串 token
15.9 Embedding 可以先理解成"查表取向量"
假设:
- token id 是 5
- 词向量维度是 128
那么 Embedding 做的事可以粗略理解为:
- 去 embedding 表里拿第 5 行
- 得到一个 128 维向量
后面会怎么用到:
- 所有 RNN / GRU / LSTM / Transformer 项目
这一步不是"生成文本",只是"把离散 id 变成可学习向量"。
15.10 RNN、GRU、LSTM、Transformer 先知道差异层级就够了
你现在不需要先背公式,只需要先知道:
- RNN:最基础的序列模型
- GRU / LSTM:为了解决长依赖问题做了改进
- Transformer:靠注意力机制处理序列,现代大模型的核心路线
- BERT:基于 Transformer 的预训练模型
后面会怎么用到:
review-analyze-rnnreview-analyze-grureview-analyze-lstmtranslation-transformerreview-analyze-bert
15.11 分类任务里,最后输出"分数"不一定是直接标签
比如二分类时:
- 模型可能先输出一个原始分数 logits
- 再经过
sigmoid - 再根据阈值判断是正类还是负类
后面会怎么用到:
review-analyze-bert/src/predict.pyreview-analyze-rnn/src/predict.py
所以你看到:
python
result = torch.sigmoid(output)
if result > 0.5:
...
就知道这是"原始输出 -> 概率 -> 最终判断"的流程。
15.12 翻译任务里,decoder_input 和 decoder_target 为什么要错一位
后面你会看到这种写法:
python
decoder_inputs = targets[:, :-1]
decoder_targets = targets[:, 1:]
你可以先把它理解成:
- 输入是"到当前时刻为止已经看到的内容"
- 目标是"下一个 token 应该是什么"
这其实就是在训练模型做"下一个 token 预测"。
后面会怎么用到:
translation-seq2seq/src/train.pytranslation-transformer/src/train.py
15.13 argmax 在生成任务里常表示"当前步选哪个 token"
python
next_token = torch.argmax(logits, dim=-1)
直觉上就是:
- 从所有候选 token 里选分数最高的那个
后面会怎么用到:
translation-seq2seq/src/predict.pytranslation-transformer/src/predict.pytranslation-attention/src/predict.py
15.14 mask 可以先理解成"哪些位置能看,哪些位置不能看"
后面 Transformer 代码里会看到:
src_pad_masktgt_maskgenerate_square_subsequent_mask(...)
你现在先记住它的直觉:
- padding 的位置不要参与有效计算
- 生成下一个 token 时,不能偷看未来位置
先把用途看懂,比先背矩阵公式更重要。
15.15 一份 AI 项目代码,你最该先回答这 6 个问题
以后打开一个新项目,先问自己:
- 这是什么任务?分类还是生成?
- 输入是什么?文本、token id,还是张量?
- 标签是什么?单个类别还是序列?
- 模型最后输出的 shape 大概是什么?
- loss 用的是什么?为什么是这个?
- 推理时最终是怎么从输出变成结果的?
如果这 6 个问题你都能回答,通常就已经不会"完全看不懂"代码了。
15.16 学到这里,你后续更该区分两件事
第一件:这是 Python 语法问题。
比如:
**inputs- 切片
for ... in ...- 类继承
第二件:这是 AI / PyTorch 框架问题。
比如:
- 为什么用
CrossEntropyLoss - 为什么要
padding - 为什么要
attention_mask - 为什么
Embedding维度这样设
把这两类问题分开,你学习时就不会一团乱麻。
15.17 本章练习
- 用自己的话解释:token、vocab、token id 三者关系。
- 用一句话说明
padding='max_length'、truncation=True、max_length=128各在做什么。 - 解释为什么翻译训练里会写
targets[:, :-1]和targets[:, 1:]。 - 说明文本分类和序列生成在"输出形式"上的主要区别。
- 打开一个后续项目的
config.py、dataset.py、model.py、train.py,分别写一句它们的职责。
第 16 章 后续课程代码阅读导航:先看什么,再看什么
这一章专门解决一个现实问题:
- 文档你已经学了
- 后面的 AI 项目代码也摆在那里
- 但一打开项目,还是容易不知道"第一眼该看哪儿"
所以这里不再讲泛知识点,而是直接告诉你:
- 一个项目先看什么
- 每个文件在干什么
- 哪些地方是 Python 语法,哪些地方是 PyTorch / AI 框架知识
16.1 先记住一个通用阅读顺序
以后你打开一个新项目,建议固定按这个顺序:
config.pydataset.py/process.pytokenizer.pymodel.pytrain.pypredict.pyevaluate.py
原因很简单:
config.py决定路径和超参数dataset.py/process.py决定数据长什么样tokenizer.py决定文本怎么变 token / idmodel.py决定模型结构train.py决定训练流程predict.py/evaluate.py决定推理和评估怎么跑
不要一上来就啃 model.py,那样最容易迷路。
16.2 先看情感分析项目:它最适合当第一个完整 AI 代码样板
建议优先看这组:
尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/review-analyze-rnn/src/尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/review-analyze-gru/src/尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/review-analyze-lstm/src/尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/review-analyze-bert/src/
为什么先看它:
- 任务清晰:输入一段文本,输出正/负情感
- 输出简单:不是生成一串文本,而是输出一个分数或类别
- 结构完整:包含数据处理、模型、训练、预测、评估
- 能把 Python + PyTorch + NLP 的基础工作流一口气串起来
16.3 情感分析项目的阅读顺序
建议这样看:
config.pyprocess.pydataset.pytokenizer.pymodel.pytrain.pypredict.pyevaluate.py
你每看一个文件,就回答一句:
- 这个文件负责数据、模型,还是流程?
16.4 情感分析项目里每个文件在干嘛
config.py
- 放目录路径、
SEQ_LEN、BATCH_SIZE、LEARNING_RATE这些超参数 - 你会看到
Path(__file__)和路径拼接
process.py
- 负责原始数据读取、清洗、切分训练集/测试集、tokenize 或 encode
- 你会看到
train_test_split、lambda、padding='max_length'、truncation=True
dataset.py
- 负责把一条条样本包装成
Dataset - 你会看到
__len__、__getitem__、DataLoader
tokenizer.py
- 负责词表构建、文本切词、id 编码、词表文件读写
- 你会看到
@classmethod、@staticmethod、word2index.get()、列表推导式
model.py
- 定义模型结构
- 你会看到
nn.Module、nn.Embedding、nn.RNN/nn.GRU/nn.LSTM/AutoModel
train.py
- 定义训练循环
- 你会看到
model.train()、loss.backward()、optimizer.step()、optimizer.zero_grad()、SummaryWriter
predict.py
- 定义单条或批量推理
- 你会看到
model.eval()、with torch.no_grad():、torch.sigmoid()、while True
evaluate.py
- 负责整体验证指标
- 你会看到把预测结果和标签收集起来再做评估
16.5 情感分析项目里,哪些地方最容易卡住
最容易卡你的点通常不是"模型原理",而是这些:
inputs = {k: v.to(device) for k, v in batch.items()}labels = inputs.pop('labels')outputs = model(**inputs)torch.sigmoid(output)BCEWithLogitsLoss
如果这些你已经在前面章节看顺了,情感分析项目就会容易很多。
16.6 第二个看 Seq2Seq 翻译:它会让你真正进入"序列生成"思维
建议接着看:
尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/translation-seq2seq/src/
为什么第二个看它:
- 比分类任务更复杂,但比 Transformer 更容易入门
- 你会第一次系统接触"输入序列 -> 输出序列"
- 能帮你理解
decoder_input、decoder_target、自回归生成这些概念
16.7 Seq2Seq 项目先看什么
建议顺序:
config.pytokenizer.pyprocess.pydataset.pymodel.pytrain.pypredict.pyevaluate.py
这里把 tokenizer.py 提前,是因为翻译任务里 tokenizer 和特殊 token 特别关键。
16.8 Seq2Seq 项目里最该盯住的几个点
看代码时重点盯这几个地方:
sos_token/eos_token/pad_tokendecoder_inputs = targets[:, :-1]decoder_targets = targets[:, 1:]torch.cat(...)argmax(...)CrossEntropyLoss(ignore_index=...)
你要先能把这些代码翻译成自然语言:
- "输入是前半段,目标是下一步"
- "每一轮生成一个 token,再拼接起来"
- "pad 位置不算 loss"
16.9 第三个看 Transformer 翻译:它更接近后面大模型思维
建议再看:
尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/translation-transformer/src/
为什么第三个看它:
- 结构比 Seq2Seq 更抽象
- 会引入位置编码、mask、Transformer encoder/decoder
- 更接近你后面理解大模型和注意力机制的代码入口
16.10 Transformer 项目里,第一轮重点只盯这几件事
先别急着深究公式,第一轮只要盯住:
nn.TransformerPositionEncodingsrc_pad_masktgt_maskgenerate_square_subsequent_mask(...)memory = encoder(...)decode(...)
先把这些代码在"流程上"看懂:
- 输入先编码
- decoder 逐步生成
- mask 控制哪些位置能看、哪些位置不能看
16.11 input-method-rnn 项目适合拿来练手"最小训练闭环"
这个项目也值得看:
尚硅谷大模型智能体线上速成班2026年01月11【itjc8.com】/代码/input-method-rnn/src/
它的价值是:
- 结构简单
- tokenizer、dataset、model、train、predict 都比较直白
- 非常适合拿来练"我能不能顺着读完一套训练代码"
如果你觉得 BERT 或 Transformer 太重,可以先拿它热身。
16.12 Hugging Face 相关内容建议怎么读
你后面还会看到:
AutoTokenizer.from_pretrained(...)AutoModel.from_pretrained(...)load_dataset(...)
这些最容易让人误以为"我是不是先得把 Hugging Face 全学完"。
其实不用。
第一轮你只要先理解:
- 它是在帮你少写底层细节
AutoTokenizer帮你处理 tokenizationAutoModel帮你加载预训练模型load_dataset帮你读取和组织数据集
先把它们当成"高级工具函数"去读就够了。
16.13 你后续读代码时,先分清"这是语法问题还是框架问题"
看到一行代码时,先问自己:
- 这是 Python 写法不懂?
- 还是 PyTorch API 不懂?
- 还是任务逻辑不懂?
举例:
model(**inputs):主要是 Python 语法CrossEntropyLoss(ignore_index=...):主要是 PyTorch / 任务知识targets[:, 1:]:主要是 Python 切片 + 张量 shapegenerate_square_subsequent_mask:主要是 Transformer 任务逻辑
把问题拆开,你学习速度会快很多。
16.14 每个项目第一轮只做"4 件事"
不要试图第一次就把所有细节啃完。第一轮只做这 4 件事:
- 说清楚任务是什么
- 说清楚输入输出各是什么
- 说清楚训练脚本主流程
- 说清楚推理脚本怎么把结果打出来
只要这 4 件事能说清,后面第二轮再回来看细节会容易很多。
16.15 本章练习
- 在
review-analyze-bert项目里,按顺序打开config.py、process.py、dataset.py、model.py、train.py,各写一句"这个文件负责什么"。 - 在
translation-seq2seq项目里,找到decoder_inputs = targets[:, :-1]和decoder_targets = targets[:, 1:],用自然语言解释它们。 - 在
translation-transformer项目里,找到src_pad_mask和tgt_mask,先不讲数学,只说明"它们分别是为了防什么问题"。 - 任选一个
predict.py,把交互循环部分完整讲一遍。
第 17 章 术语速查表:后续 AI / PyTorch 学习高频词一页通
这一章不是教程,而是速查表。
以后你看代码、看笔记、看课程视频时,看到这些词可以快速回来查,不用每次都重新搜。
17.1 Tensor
- 多维数据容器
- 可以先把它理解成"带计算能力的多维数组"
- 后面模型输入输出基本都是它
17.2 shape
- 数据外形
- 比如
[batch_size, seq_len] - 是理解模型代码最重要的线索之一
17.3 batch / batch_size
- 一批样本
batch_size表示一批包含多少条数据- 训练通常不是一条一条跑,而是一批一批跑
17.4 seq_len
- 序列长度
- 在 NLP 里通常表示一句话有多少 token
17.5 vocab / vocab_size
- 词表 / 词表大小
- 词表把 token 映射成 id
17.6 token / token id
- token:切分后的最小文本单位
- token id:token 对应的整数编号
17.7 tokenizer
- 把文本转成 token / id 的工具
- 有时也负责把 id 还原成文本
17.8 padding
- 补齐
- 把不同长度样本补到一致长度,方便组成 batch
17.9 truncation
- 截断
- 文本太长时裁剪掉后面一部分
17.10 max_length
- 最大长度
- 常与 padding / truncation 一起出现
17.11 attention_mask
- 标记哪些位置是真实内容,哪些位置是 padding
- BERT 输入里很常见
17.12 token_type_ids
- 区分句子 A / 句子 B 的输入标记
- 在句对任务里更常见
17.13 return_tensors='pt'
- tokenizer 输出直接转成 PyTorch tensor
'pt'就是 PyTorch
17.14 Dataset
- 单条数据如何取出来的定义
- 通常配合
__len__和__getitem__
17.15 DataLoader
- 按 batch 提供数据
- 可以打乱、迭代、拼 batch
17.16 collate_fn
- 自定义"一个 batch 怎么拼出来"
- 处理变长序列时很常见
17.17 pad_sequence
- 把不同长度序列补齐后再拼成 batch
- 翻译任务里很常见
17.18 nn.Module
- PyTorch 模型类的标准父类
- 你定义模型通常都要继承它
17.19 forward
- 定义前向传播逻辑的方法
- 决定输入怎么流到输出
17.20 Embedding
- 把 token id 映射成向量
- 可以先理解成"查表取向量"
17.21 hidden_size / embedding_dim / dim_model
- 都是向量维度相关参数
- 名字不同,但本质都在描述"表示空间大小"
17.22 logits
- 模型直接输出的原始分数
- 不是最终概率
17.23 sigmoid
- 常用于二分类,把 logits 映射到 0 到 1
17.24 softmax
- 常用于多分类或注意力权重归一化
- 把一组分数变成概率分布
17.25 argmax
- 取最大值所在位置
- 常用于"选当前最可能的类别 / token"
17.26 BCEWithLogitsLoss
- 常用于二分类
- 输入通常是 logits,不需要你先手动做 sigmoid 再喂给 loss
17.27 CrossEntropyLoss
- 常用于多分类或序列预测
- 翻译、输入法预测里经常见到
17.28 ignore_index
- 告诉 loss 忽略某些目标位置
- 通常用来忽略 pad token
17.29 optimizer
- 优化器
- 负责根据梯度更新模型参数
17.30 loss.backward()
- 反向传播
- 计算梯度
17.31 optimizer.step()
- 更新参数
17.32 optimizer.zero_grad()
- 清空旧梯度
- 避免梯度累计污染下一轮
17.33 model.train() / model.eval()
train():训练模式eval():评估/推理模式
17.34 torch.no_grad()
- 推理时关闭梯度记录
- 节省资源
17.35 state_dict
- 模型参数字典快照
- 训练保存、预测加载时都会用到
17.36 SummaryWriter / TensorBoard
- 用来记录训练过程,比如 loss 曲线
17.37 RNN / GRU / LSTM
- 经典序列模型
- GRU、LSTM 是 RNN 的改进版
17.38 Transformer
- 基于注意力机制的序列模型
- 大模型路线的核心基础之一
17.39 BERT
- 基于 Transformer 的预训练语言模型
- 你后面课程里的情感分析项目会用到
17.40 mask
- 控制哪些位置参与计算、哪些位置被遮住
- Transformer 里非常关键
17.41 config.py
- 项目配置入口
- 常放路径、超参数、常量
17.42 train.py / predict.py / evaluate.py
train.py:训练predict.py:推理/交互evaluate.py:评估
17.43 Python 语法问题 vs 框架问题
以后卡住时先问自己:
- 这是 Python 语法不懂?
- 还是 PyTorch API 不懂?
- 还是任务逻辑不懂?
先把问题归类,学习效率会高很多。
第 18 章 学完这份教程以后,下一步怎么练
建议直接做 4 类小练习:
18.1 命令行小工具
比如:
- 记账脚本
- 批量重命名文件
- 文本统计
- CSV 清洗
18.2 文件处理脚本
比如:
- 批量读取目录中的文件
- 替换文本
- 合并 Markdown
- 导出结果到新文件
18.3 数据结构练习
比如:
- 学生成绩统计
- 单词频次统计
- 商品库存统计
18.4 简单面向对象练习
比如:
- 学生管理系统
- 图书借阅系统
- 订单对象与支付对象建模
第 19 章 一页速记
python
name = 'Alice'
age = 18
if age >= 18:
print('成年人')
nums = [1, 2, 3]
user = {'name': 'Alice', 'age': 18}
tags = {'python', 'java'}
for n in nums:
print(n)
def add(a: int, b: int) -> int:
return a + b
class Person:
def __init__(self, name: str):
self.name = name
def speak(self) -> None:
print(self.name)
with open('a.txt', 'rt', encoding='utf-8') as f:
print(f.read())
最后给 Java 开发者的建议
真正阻碍你学 Python 的,通常不是"语法记不住",而是这几种惯性:
- 总想先设计完整类型体系
- 总想把代码写成 Java 的组织方式
- 总想先把高级特性学完再动手
更好的顺序是:
- 先能写脚本
- 再能写小项目
- 再逐步学 Pythonic 写法
- 最后再补高级机制
如果你愿意,把这份教程当成第一轮学习主线,把完整原稿当成"查漏补缺的参考书",效果会比直接啃原始大而全资料更好。