Python 入门教程(面向有 Java 经验的开发者)

Python 入门教程(面向有 Java 经验的开发者)

这不是一份"Python 知识点罗列",而是一份可以顺着学下去的教程。

目标:让已经有 Java 经验的开发者,在最短时间内建立 Python 的开发直觉,并能开始写脚本、做小工具、看懂常见 Python 项目代码。

这份教程怎么用

建议按顺序学习,不要跳着看。

每一章都尽量遵循同一种结构:

  • 先讲这一章到底要学会什么
  • 再给最小必要知识
  • 再给 Java 对照,帮助你迁移已有经验
  • 最后给练习,帮助你真正掌握

推荐节奏:

  • 每次学 1 章
  • 先自己照着敲代码,不要只看
  • 每章末尾练习至少做 2 题
  • 卡住时回看原始完整稿:尚硅谷大模型技术之 Python(V3.0).md

学习路线总览

如果你有 Java 经验,第一轮只需要拿下这 7 件事:

  1. Python 的基本语法和缩进规则
  2. 常用数据类型与容器
  3. 条件、循环、函数
  4. 字符串、切片、推导式
  5. Python 风格的面向对象
  6. 模块、异常、文件操作
  7. 一些最常见的 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 本章练习

  1. 创建 calc.py,打印 3 * 7 的结果。
  2. 进入 Python 交互模式,试一下:10 / 310 // 310 % 3
  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 最常见的基础类型

你第一轮最需要掌握这几个:

  • int
  • float
  • bool
  • str
  • None

示例:

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 个观念切换:

  1. Python 不写变量类型,但对象有类型。
  2. Python 用缩进表达层级,不用 {}
  3. 类型转换需要主动做,尤其是 input() 之后。
  4. 逻辑运算写 and / or / not

这几个点适应了,后面会顺很多。

2.9 本章练习

  1. 定义变量 nameagesalary,分别保存你的名字、年龄、薪资。
  2. 打印这 3 个变量的类型。
  3. 写一个 if 判断:如果年龄大于等于 18,打印"成年人",否则打印"未成年人"。
  4. 让用户输入一个数字字符串,把它转成 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 本章练习

  1. 让用户输入姓名和年龄,然后打印:你好,xxx,你今年 xx 岁。
  2. 输入一个带前后空格的字符串,打印去空格后的结果。
  3. 输入一个英文单词,打印它的大写形式。

第 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 本章练习

  1. 定义一个列表,保存 5 个分数,打印第 1 个和最后 1 个分数。
  2. 定义一个字典,保存一个人的 nameagecity
  3. 定义两个集合,输出它们的并集和交集。
  4. for key, value in ... 的方式遍历字典。
  5. 对一个数字列表分别输出长度、最大值、最小值和元素总和。
  6. 写一个小字典,使用 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

如果只是遍历一个容器,通常 forwhile 更自然。

5.5 breakcontinue 必须会

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('空列表')

这些值通常会被当成假:

  • None
  • 0
  • 0.0
  • ''
  • []
  • {}
  • set()

这也是 Python 代码看起来更简洁的原因之一。

5.7 本章练习

  1. 输入一个分数,输出 A/B/C。
  2. for 打印 1 到 10。
  3. while 求 1 到 100 的和。
  4. 判断一个列表是否为空,并打印提示信息。
  5. 遍历 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 本章练习

  1. 写一个函数,接收两个数字,返回较大值。
  2. 写一个函数,接收姓名和问候语,问候语默认为"你好"。
  3. 写一个函数,接收任意多个数字,返回它们的和。
  4. 写一个函数,返回姓名和年龄两个值,并在外部解包接收。
  5. 写一个递归函数,计算 n 的阶乘。
  6. 写一个接收字典参数的函数,并用 **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. 把一个列表倒序输出。
  2. 用列表推导式生成 1 到 10 的平方列表。
  3. 从列表中筛选所有偶数。
  4. 对一组字典按 age 排序。
  5. 用条件表达式判断一个数字是奇数还是偶数。

第 8 章 面向对象:保留 Java 开发者真正需要的部分

你要学会什么

学完这一章,你应该能:

  • 定义类和实例
  • 理解 self__init__
  • 使用继承和方法重写
  • 看懂 super() 和常见特殊方法
  • 理解 Python 的鸭子类型与 Java 接口思维的不同
  • 看懂后续 AI 代码里的 nn.ModuleDataset、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 本章练习

  1. 写一个 Student 类,包含 namescore
  2. Student 类加一个 show() 方法,打印学生信息。
  3. 写一个 AnimalDog,让 Dog 重写 speak()
  4. 写一个函数,接收任意有 speak() 方法的对象并调用它。
  5. 写一个类,实现 __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 本章练习

  1. 创建 math_utils.py,写一个加法函数。
  2. 在另一个文件中导入并调用这个函数。
  3. 尝试给模块起别名导入。
  4. 写一个带 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 本章练习

  1. 输入一个数字并输出 100 / num,处理输入错误和除零错误。
  2. 写一个函数,当年龄小于 0 时抛出异常。
  3. 试着分别触发 ValueErrorZeroDivisionError
  4. 自定义一个异常类,用来表示"用户名不能为空"。

第 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 本章练习

  1. 读取一个文本文件,并打印内容。
  2. 把一段字符串写入另一个文件。
  3. 使用 Path 判断某个文件是否存在。
  4. 创建一个目录,并遍历这个目录下的内容。
  5. 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 密集型任务更常考虑多进程。等你真正遇到性能问题、批量处理任务、爬虫、并发下载时再深入,会更容易建立直觉。

更好的学习顺序是:

  1. 先把函数、容器、模块、异常、文件操作写熟。
  2. 再写几个真实脚本,体会"代码组织"和"数据流动"。
  3. 然后回头学装饰器、闭包、迭代器、生成器。
  4. 最后再进并发:线程、进程、协程。

这样再学这些高级主题,你不会只记住名词,而是能理解它们到底在解决什么问题。


第 13 章 面向 AI 代码的前置语法补充

这一章是专门按你后面要学的 AI 代码反推出来的。

目标不是继续扩大知识点范围,而是提前把"你以后一定会反复见到的 Python 写法"单独讲清楚,这样你后面看训练代码、推理代码、Tokenizer、Dataset、Model 代码时,不会卡在语法上。

13.1 先把后面的 AI 项目代码拆成 5 类文件来看

你后面看到的大多数项目,代码结构其实都很像:

  • config.py:放路径、超参数、常量
  • dataset.py:准备数据、定义 Dataset / DataLoader
  • tokenizer.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 squeezeunsqueezereshape:先把名字和形状变化读懂

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()}

这句可以拆成三层意思:

  1. 遍历字典里的每组 key, value
  2. 对每个 value 调用 .to(device)
  3. 再组装成一个新字典

如果你能把这种推导式看顺,后面的训练代码阅读难度会下降很多。

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_idsattention_masktoken_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():从词表文件创建 tokenizer
  • build_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 本章练习

  1. 写一个 while True 的小交互脚本,输入 q 退出,空字符串跳过。
  2. 写一个字典推导式,把 {'a': 1, 'b': 2} 变成 {'a': 10, 'b': 20}
  3. 写一个函数,定义参数 name, age,然后用 **user 的方式调用它。
  4. 写一个类,实现 __len____getitem__
  5. 写一段切片代码,分别演示 [:, :-1][:, 1:][:, -1] 这三种形式在"二维列表语义"里的含义。

第 14 章 PyTorch 最小必备:先做到能读懂训练代码

这一章不是要把 PyTorch 讲成框架手册,而是按你后面课程代码里最常出现的模式来讲。

目标非常明确:

  • 先能看懂 train.pypredict.pymodel.py
  • 先知道一段 PyTorch 代码到底在"准备数据 / 前向传播 / 算损失 / 反向传播 / 保存模型"的哪一步
  • 先不在 tensorshapedeviceloss 这些词上卡住

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:每个样本有多少个 token
  • vocab_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.py
  • translation-transformer/src/dataset.py
  • for inputs, targets in dataloader:

14.5 collate_fnpad_sequence:为变长序列凑成一个 batch

python 复制代码
from torch.nn.utils.rnn import pad_sequence

为什么需要它:

  • 文本长度不一样
  • 一个 batch 里的句子必须整理成同样长度,才能堆叠起来

后面会怎么用到:

  • translation-seq2seq/src/dataset.py
  • translation-transformer/src/dataset.py

如果你后面看到:

  • collate_fn=batch_fn
  • pad_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.pynn.Embedding + nn.RNN
  • review-analyze-gru/src/model.pynn.GRU
  • review-analyze-lstm/src/model.pynn.LSTM
  • translation-transformer/src/model.pynn.Transformer
  • review-analyze-bert/src/model.pyAutoModel + 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()

你可以把它背成固定模板:

  1. 切到训练模式 model.train()
  2. 取一批数据
  3. 做前向传播 model(...)
  4. 算损失 loss_fn(...)
  5. 反向传播 loss.backward()
  6. 更新参数 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.pyevaluate.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.pytorch.sigmoid(output)
  • translation-seq2seq/src/predict.pytorch.argmax(decoder_output, dim=-1)
  • translation-attention/src/model.pytorch.softmax(attention_scores, dim=-1)

14.13 两种常见 loss,要先能区分场景

后续代码里最常见的就是这两个:

  • BCEWithLogitsLoss:二分类,输入通常是 logits,标签常是 0/1
  • CrossEntropyLoss:多分类或序列分类,目标通常是类别索引

后面会怎么用到:

  • 情感分析: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 代码时的阅读顺序

建议你按这个顺序看:

  1. 先看 config.py,知道路径和超参数
  2. 再看 dataset.py,知道一条样本长什么样
  3. 再看 model.py,知道输入输出 shape 变化
  4. 再看 train.py,理解训练流程
  5. 最后看 predict.py / evaluate.py

这样比一上来啃 model.py 顺很多。

14.18 本章练习

  1. torch.tensor() 创建一个二维 tensor,并打印它的 shape。
  2. 写一个最小的 nn.Module 类,里面只放一个 Linear 层。
  3. 写出一个伪训练循环,至少包含 model.train()loss.backward()optimizer.step()optimizer.zero_grad()
  4. 写一段推理代码,包含 model.eval()with torch.no_grad():
  5. 用一句话解释 BCEWithLogitsLossCrossEntropyLoss 各更适合什么任务。

第 15 章 面向 AI 项目的最小知识地图:Tokenizer、任务、训练流程

这一章不讲深理论,只讲你后面看 NLP / AI 项目代码时最先需要的概念地图。

目标是:

  • 知道这些 AI 术语到底在代码里落到哪里
  • 知道一份 NLP 项目代码大概在解决什么问题
  • 不会因为概念名词太多而看不懂项目结构

15.1 文本进入模型前,通常先经过这条流水线

最常见的流程是:

  1. 原始文本
  2. tokenizer 分词/切词
  3. token 转 id
  4. 补齐或截断长度
  5. 组成 batch
  6. 喂给模型
  7. 模型输出 logits / 概率 / 类别

后面会怎么用到:

  • process.py
  • tokenizer.py
  • predict.py

15.2 tokenizer 到底在做什么

可以先把 tokenizer 理解成:

  • 把"人类文本"变成"模型能处理的 token 序列"

常见动作:

  • tokenize():切词
  • encode():转成 id
  • decode():把 id 还原成文本

后面会怎么用到:

  • JiebaTokenizer
  • ChineseTokenizer
  • EnglishTokenizer
  • AutoTokenizer.from_pretrained(...)

15.3 词表 vocab 和几个特殊 token 必须认识

后面你会经常看到:

  • unk_token:未知词
  • pad_token:补齐占位
  • sos_token:序列开始
  • eos_token:序列结束

这些不是"语法点",但如果不认识,后面翻译代码很容易看不懂。

后面会怎么用到:

  • input-method-rnn/src/tokenizer.py
  • translation-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.py
  • review-analyze-bert/src/predict.py
  • Hugging Face notebook 里的 tokenizer 示例

15.5 attention_masktoken_type_ids 可以先知道用途,不必先深挖原理

你后面在 BERT 输入里会看到这些字段:

  • input_ids
  • attention_mask
  • token_type_ids

先这样理解就够:

  • input_ids:真正的 token id
  • attention_mask:哪些位置是有效内容,哪些位置是 padding
  • token_type_ids:句子对任务中区分句子 A / B

后面会怎么用到:

  • review-analyze-bert/src/process.py
  • review-analyze-bert/src/train.py
  • review-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-rnn
  • review-analyze-gru
  • review-analyze-lstm
  • translation-transformer
  • review-analyze-bert

15.11 分类任务里,最后输出"分数"不一定是直接标签

比如二分类时:

  • 模型可能先输出一个原始分数 logits
  • 再经过 sigmoid
  • 再根据阈值判断是正类还是负类

后面会怎么用到:

  • review-analyze-bert/src/predict.py
  • review-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.py
  • translation-transformer/src/train.py

15.13 argmax 在生成任务里常表示"当前步选哪个 token"

python 复制代码
next_token = torch.argmax(logits, dim=-1)

直觉上就是:

  • 从所有候选 token 里选分数最高的那个

后面会怎么用到:

  • translation-seq2seq/src/predict.py
  • translation-transformer/src/predict.py
  • translation-attention/src/predict.py

15.14 mask 可以先理解成"哪些位置能看,哪些位置不能看"

后面 Transformer 代码里会看到:

  • src_pad_mask
  • tgt_mask
  • generate_square_subsequent_mask(...)

你现在先记住它的直觉:

  • padding 的位置不要参与有效计算
  • 生成下一个 token 时,不能偷看未来位置

先把用途看懂,比先背矩阵公式更重要。

15.15 一份 AI 项目代码,你最该先回答这 6 个问题

以后打开一个新项目,先问自己:

  1. 这是什么任务?分类还是生成?
  2. 输入是什么?文本、token id,还是张量?
  3. 标签是什么?单个类别还是序列?
  4. 模型最后输出的 shape 大概是什么?
  5. loss 用的是什么?为什么是这个?
  6. 推理时最终是怎么从输出变成结果的?

如果这 6 个问题你都能回答,通常就已经不会"完全看不懂"代码了。

15.16 学到这里,你后续更该区分两件事

第一件:这是 Python 语法问题。

比如:

  • **inputs
  • 切片
  • for ... in ...
  • 类继承

第二件:这是 AI / PyTorch 框架问题。

比如:

  • 为什么用 CrossEntropyLoss
  • 为什么要 padding
  • 为什么要 attention_mask
  • 为什么 Embedding 维度这样设

把这两类问题分开,你学习时就不会一团乱麻。

15.17 本章练习

  1. 用自己的话解释:token、vocab、token id 三者关系。
  2. 用一句话说明 padding='max_length'truncation=Truemax_length=128 各在做什么。
  3. 解释为什么翻译训练里会写 targets[:, :-1]targets[:, 1:]
  4. 说明文本分类和序列生成在"输出形式"上的主要区别。
  5. 打开一个后续项目的 config.pydataset.pymodel.pytrain.py,分别写一句它们的职责。

第 16 章 后续课程代码阅读导航:先看什么,再看什么

这一章专门解决一个现实问题:

  • 文档你已经学了
  • 后面的 AI 项目代码也摆在那里
  • 但一打开项目,还是容易不知道"第一眼该看哪儿"

所以这里不再讲泛知识点,而是直接告诉你:

  • 一个项目先看什么
  • 每个文件在干什么
  • 哪些地方是 Python 语法,哪些地方是 PyTorch / AI 框架知识

16.1 先记住一个通用阅读顺序

以后你打开一个新项目,建议固定按这个顺序:

  1. config.py
  2. dataset.py / process.py
  3. tokenizer.py
  4. model.py
  5. train.py
  6. predict.py
  7. evaluate.py

原因很简单:

  • config.py 决定路径和超参数
  • dataset.py / process.py 决定数据长什么样
  • tokenizer.py 决定文本怎么变 token / id
  • model.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 情感分析项目的阅读顺序

建议这样看:

  1. config.py
  2. process.py
  3. dataset.py
  4. tokenizer.py
  5. model.py
  6. train.py
  7. predict.py
  8. evaluate.py

你每看一个文件,就回答一句:

  • 这个文件负责数据、模型,还是流程?

16.4 情感分析项目里每个文件在干嘛

config.py

  • 放目录路径、SEQ_LENBATCH_SIZELEARNING_RATE 这些超参数
  • 你会看到 Path(__file__) 和路径拼接

process.py

  • 负责原始数据读取、清洗、切分训练集/测试集、tokenize 或 encode
  • 你会看到 train_test_splitlambdapadding='max_length'truncation=True

dataset.py

  • 负责把一条条样本包装成 Dataset
  • 你会看到 __len____getitem__DataLoader

tokenizer.py

  • 负责词表构建、文本切词、id 编码、词表文件读写
  • 你会看到 @classmethod@staticmethodword2index.get()、列表推导式

model.py

  • 定义模型结构
  • 你会看到 nn.Modulenn.Embeddingnn.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_inputdecoder_target、自回归生成这些概念

16.7 Seq2Seq 项目先看什么

建议顺序:

  1. config.py
  2. tokenizer.py
  3. process.py
  4. dataset.py
  5. model.py
  6. train.py
  7. predict.py
  8. evaluate.py

这里把 tokenizer.py 提前,是因为翻译任务里 tokenizer 和特殊 token 特别关键。

16.8 Seq2Seq 项目里最该盯住的几个点

看代码时重点盯这几个地方:

  • sos_token / eos_token / pad_token
  • decoder_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.Transformer
  • PositionEncoding
  • src_pad_mask
  • tgt_mask
  • generate_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 帮你处理 tokenization
  • AutoModel 帮你加载预训练模型
  • load_dataset 帮你读取和组织数据集

先把它们当成"高级工具函数"去读就够了。

16.13 你后续读代码时,先分清"这是语法问题还是框架问题"

看到一行代码时,先问自己:

  • 这是 Python 写法不懂?
  • 还是 PyTorch API 不懂?
  • 还是任务逻辑不懂?

举例:

  • model(**inputs):主要是 Python 语法
  • CrossEntropyLoss(ignore_index=...):主要是 PyTorch / 任务知识
  • targets[:, 1:]:主要是 Python 切片 + 张量 shape
  • generate_square_subsequent_mask:主要是 Transformer 任务逻辑

把问题拆开,你学习速度会快很多。

16.14 每个项目第一轮只做"4 件事"

不要试图第一次就把所有细节啃完。第一轮只做这 4 件事:

  1. 说清楚任务是什么
  2. 说清楚输入输出各是什么
  3. 说清楚训练脚本主流程
  4. 说清楚推理脚本怎么把结果打出来

只要这 4 件事能说清,后面第二轮再回来看细节会容易很多。

16.15 本章练习

  1. review-analyze-bert 项目里,按顺序打开 config.pyprocess.pydataset.pymodel.pytrain.py,各写一句"这个文件负责什么"。
  2. translation-seq2seq 项目里,找到 decoder_inputs = targets[:, :-1]decoder_targets = targets[:, 1:],用自然语言解释它们。
  3. translation-transformer 项目里,找到 src_pad_masktgt_mask,先不讲数学,只说明"它们分别是为了防什么问题"。
  4. 任选一个 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 的组织方式
  • 总想先把高级特性学完再动手

更好的顺序是:

  1. 先能写脚本
  2. 再能写小项目
  3. 再逐步学 Pythonic 写法
  4. 最后再补高级机制

如果你愿意,把这份教程当成第一轮学习主线,把完整原稿当成"查漏补缺的参考书",效果会比直接啃原始大而全资料更好。

相关推荐
草莓熊Lotso2 小时前
Linux C++ 高并发编程:从原理到手撕,线程池全链路深度解析
linux·运维·服务器·开发语言·数据库·c++·mysql
峥嵘life2 小时前
Android 切换用户后无法获取 MAC 地址分析解决
android·python·macos
小毛驴8502 小时前
命令行中使用 Maven 启动 Spring Boot 应用
java·spring boot·maven
小王师傅662 小时前
【Java结构化梳理】泛型-上
java·开发语言
m0_613856292 小时前
mysql如何使用IF函数_mysql简单二元逻辑转换
jvm·数据库·python
爱喝热水的呀哈喽2 小时前
5步创建一个有不同numpy scipy版本的python环境
python·numpy·scipy
歪楼小能手2 小时前
Android16在开机向导最后添加一个声明界面
android·java·平板
TE-茶叶蛋2 小时前
Maven install 的原理
java·maven