LLM成长笔记(一):Python 核心知识点
Python 核心知识点全景学习博客(含详细代码注释 · AI应用强化版)
这篇博客专为初学者设计,每个知识点都从一个实际问题出发,通过生活化的例子帮你建立感性认识,再深入浅出地讲解核心原理,最后配合带详尽注释的演示用例和输出结果加深理解。
此外,每个知识点末尾都增加了 AI 应用场景提示,帮助你理解这些基础在 AI 开发中究竟用在哪里。
一、初级篇
1. 数据类型
问题
你有一堆信息要处理:年龄、姓名、购物清单、学生成绩表……如何让计算机正确区分并灵活操作它们?
生活化类比
想象你有一个分类储物箱:
- 整数、浮点数像单个数字标签,写上去就不能擦掉重写,只能换新的标签。
- 字符串像刻字的木牌,想改只能重新刻一块。
- 列表像可随意增减物品的购物车,能随时添加、删除或更换商品。
- 元组像已封箱的快递盒,装好就不能再变动。
- 字典像根据姓名找人的通讯录,通过“键”快速定位到“值”。
- 集合像去重后的兴趣标签,每个元素唯一且无序。
原理
Python 中一切数据都是“对象”,每个对象有固定的类型,决定它能做什么。
- 不可变对象(int、float、str、tuple):一旦创建,内部值不能改变。修改操作其实会生成新对象,类似用新标签替换旧标签。
- 可变对象(list、dict、set):允许原地增删改内容,就像在同一个购物车里增减物品。
这种区分让 Python 在内存管理上更清晰,也影响函数传参是“传引用”还是“传值”。
演示用例(带注释)
# ---- 基本不可变类型 ----
age: int = 25 # 整数:年龄
price: float = 19.99 # 浮点数:价格
name: str = "Alice" # 字符串:名字
is_student: bool = True # 布尔值:是否为学生
# 使用 type() 查看每个变量的实际类型
print(type(age), age) # <class 'int'> 25
print(type(price), price) # <class 'float'> 19.99
print(type(name), name) # <class 'str'> Alice
print(type(is_student), is_student) # <class 'bool'> True
# ---- 可变容器类型 ----
fruits: list = ["apple", "banana", "cherry"] # 列表:可以修改的序列
point: tuple = (3, 4) # 元组:不可修改的序列
user: dict = {"name": "Bob", "age": 30} # 字典:键值对集合
tags: set = {"python", "learning", "blog"} # 集合:无序、不重复的元素
print(fruits[0]) # 列表按索引取值:第一个元素
print(point[1]) # 元组按索引取值:第二个元素
print(user["name"]) # 字典通过键取值
print("python" in tags) # 集合成员检测:True 表示存在
# ---- 常用进阶操作(AI 开发必备) ----
# 字典的安全取值(避免 KeyError)
config = {"model": "gpt-4", "temperature": 0.7}
# 若字段不存在,使用默认值
top_p = config.get("top_p", 1.0)
print("top_p 取值:", top_p)
# 列表推导式(高效处理批量数据)
scores = [0.2, 0.8, 0.95, 0.4]
# 筛选出高于 0.5 的得分
high_scores = [s for s in scores if s > 0.5]
print("大于 0.5 的分数:", high_scores)
# 切片操作(取前 N 个元素)
top3 = scores[:3] # 从索引 0 到 2
print("前三个分数:", top3)
输出结果
<class 'int'> 25
<class 'float'> 19.99
<class 'str'> Alice
<class 'bool'> True
apple
4
Bob
True
top_p 取值: 1.0
大于 0.5 的分数: [0.8, 0.95]
前三个分数: [0.2, 0.8, 0.95]
AI 应用场景:解析 API 返回的 JSON 数据时,经常需要从字典里安全地提取字段(例如提取模型的生成结果、置信度)。列表推导式和切片用于批量处理推理结果、选取 Top-K 预测。
2. 控制流
问题
程序不能总是直线执行,有时需要“如果…就…否则…”,有时要重复做一件事直到条件改变。如何让代码学会“思考”?
生活化类比
- 分支像是出门看天气:如果下雨,带伞;否则,戴帽子。
- for 循环像按花名册依次点名,点完最后一个自动结束。
- while 循环像在自动售货机前投币,只要余额不够就一直提醒你投钱,够了才出货。
原理
if-elif-else根据条件表达式(结果为 True/False)选择执行哪段代码。for循环本质上是在遍历一个“可迭代对象”(如列表、字符串等),每一次取出下一个元素,直到取完。while循环则反复检查一个条件,只要条件成立就继续执行循环体。break能立即终止整个循环,continue则跳过当前这一次迭代,直接进入下一次。
演示用例(带注释)
# ---- 条件分支 ----
score = 85
if score >= 90:
grade = "A"
elif score >= 75:
grade = "B"
else:
grade = "C"
print(f"成绩等级:{grade}")
# ---- for 循环:遍历范围 ----
for i in range(1, 4): # range(1,4) 产生 1,2,3
print(f"循环第{i}次")
# ---- while 循环 + break/continue ----
count = 0
while True: # 无限循环,必须靠 break 退出
if count >= 3:
break # 彻底终止循环
if count == 1:
count += 1
continue # 跳过本次,不执行后面的打印
print(f"计数:{count}")
count += 1
输出结果
成绩等级:B
循环第1次
循环第2次
循环第3次
计数:0
计数:2
AI 应用场景:批量处理数据时,
break用于提前终止不满足条件的批次(如检测到异常样本);continue用于跳过无效数据。循环结构是处理数据集、遍历模型输出列表的基础。
3. 函数
问题
你有一段经常要用的操作(比如计算运费、生成问候语),每次都重写一遍太麻烦,如何打包复用?
生活化类比
函数就像厨房里的搅拌机:你放进食材(参数),它按设定好的模式运行,最后倒出果汁(返回值)。不同的食材组合可以做出不同的饮品。
原理
- 用
def定义一个函数,给它起个名字,指定参数和返回值。 - 调用函数时,Python 会暂停当前工作,跳转到函数内部执行,完成后再跳回原处,并将
return后的结果带回来。 - 参数可以设定默认值,但默认值只在函数定义时求值一次,因此默认值不要用可变对象(如列表、字典),否则多次调用会共享同一个对象,导致难以发现的 bug。
- 正确做法:将默认值设为
None,在函数体内再创建新的可变对象。
演示用例(带注释)
def greet(name: str, greeting: str = "Hello") -> str:
"""返回问候语,greeting 可自定义"""
return f"{greeting}, {name}!"
print(greet("Alice"))
print(greet("Bob", greeting="Hi"))
# ---- 危险示例:可变对象作默认参数(绝对不要这样写!) ----
def add_to_list_bad(item, my_list=[]):
my_list.append(item)
return my_list
# 两次调用“看起来”应该是独立的,但实际上共享了同一个列表
print(add_to_list_bad(1)) # [1]
print(add_to_list_bad(2)) # [1, 2] <- 可能不是你想要的结果!
# ---- 正确的写法:用 None 作为占位符 ----
def add_to_list_good(item, my_list=None):
if my_list is None:
my_list = [] # 每次调用都创建全新列表
my_list.append(item)
return my_list
print(add_to_list_good(1)) # [1]
print(add_to_list_good(2)) # [2] <- 互不影响
输出结果
Hello, Alice!
Hi, Bob!
[1]
[1, 2]
[1]
[2]
AI 应用场景:调用大模型 API 时,几乎每个函数都包含大量可选参数(如 temperature、top_p、max_tokens),默认参数和关键字参数能让你的调用代码简洁且安全。避免可变默认参数可防止“缓存污染”这类隐蔽错误。
4. 面向对象
问题
你需要用代码模拟现实世界的事物,比如不同种类的动物,它们有共性又各有特性,如何组织?
生活化类比
- 类就是模具,比如“手机模具”;对象就是用模具造出的一部部真实手机。
- 继承就像“智能手机”继承了“手机”的所有基础功能,还新增了触屏和上网。
- 多态:同样一个“开机”按钮,不同品牌的手机开机画面不同,但操作方式一样。
原理
class定义类,里面包含属性(数据)和方法(操作数据的行为)。__init__是构造函数,创建对象时自动运行,用于初始化属性。- 子类继承父类后,可以用
super()调用父类方法,在其基础上进行扩展。 - 重写方法实现了多态。
- 特殊方法(魔法方法)如
__str__可以定义对象被print时的输出,极大方便调试。
演示用例(带注释)
# 基类(父类):动物
class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
return "..."
def __str__(self) -> str: # 魔法方法:定义 print 时的显示
return f"动物:{self.name}"
# 子类 Dog 继承 Animal,并重写 speak 方法
class Dog(Animal):
def speak(self) -> str:
return f"{self.name} says Woof!"
def __str__(self) -> str:
return f"狗:{self.name}"
# 子类 Cat 同样继承并重写 speak
class Cat(Animal):
def speak(self) -> str:
return f"{self.name} says Meow!"
# 子类扩展父类方法(使用 super)
class LoudDog(Dog):
def speak(self) -> str:
base_sound = super().speak() # 先获得父类的叫声
return base_sound.upper() + "!!!"
# 多态体现:不同对象调用同一个方法名,产生不同行为
animals = [Dog("Buddy"), Cat("Kitty"), LoudDog("Max")]
for a in animals:
print(a.speak())
print(a) # 自动调用 __str__
输出结果
Buddy says Woof!
狗:Buddy
Kitty says Meow!
动物:Kitty
BUDDY SAYS WOOF!!!!
狗:Max
AI 应用场景:封装模型加载器、自定义数据集类、日志记录器等常使用继承 +
super()。__str__让你能直观地打印模型配置、数据样本信息。在复杂系统中,也可以将功能拆分成独立类再组合(例如一个DataLoader类包含一个Tokenizer对象),比多层继承更灵活。
5. 异常处理
问题
程序运行时可能遇到意外情况:除零、文件找不到、API 返回格式错误……不想让程序直接崩溃,怎么优雅地处理?
生活化类比
像在自动门前进出:
try块是你尝试走进门。- 如果门失灵夹到你了(发生异常),
except就是一旁的急救员,立刻处理。 - 如果顺利通过(无异常),
else会给你一个微笑。 - 不管发生什么,
finally都会把门关上(清理资源)。
原理
- 异常是错误信号,Python 遇到问题就会抛出(raise)一个异常对象。
- 使用多个
except分支可以分别处理不同类型的异常。 - 使用
traceback模块可以打印完整的错误堆栈,方便调试。 else仅在没发生异常时运行;finally总是运行,常用于释放资源。
演示用例(带注释)
import traceback
def divide(a: float, b: float) -> float | None:
try:
result = a / b
except ZeroDivisionError:
print("错误:除数不能为零!")
return None
except TypeError as e: # 捕获类型错误
print(f"类型错误:{e}")
traceback.print_exc() # 打印完整堆栈,方便定位
return None
else:
print("计算成功")
return result
finally:
print("执行完毕")
print(divide(10, 2))
print("---")
print(divide(10, 0))
print("---")
print(divide("10", 2)) # 触发 TypeError
输出结果
计算成功
执行完毕
5.0
---
错误:除数不能为零!
执行完毕
None
---
类型错误:unsupported operand type(s) for /: 'str' and 'int'
Traceback (most recent call last):
File "demo.py", line 4, in divide
result = a / b
TypeError: unsupported operand type(s) for /: 'str' and 'int'
执行完毕
None
AI 应用场景:调用外部 API 时可能遇到网络超时、返回格式错误、模型推理失败等异常,需要分别捕获并记录。多分支异常处理和完整的堆栈信息是 7x24 小时服务的调试基础。
6. 文件 I/O
问题
你想让程序保存数据到硬盘,下次还能读出来。如何安全地读写文件,避免忘记关闭?
生活化类比
用 with open 就像用银行保险柜:
- 你申请打开保险柜(打开文件),在里面存取物品(读写)。
- 操作完成后,无论你是否弄乱东西(发生异常),银行都会自动关好柜门(关闭文件)。
原理
open()函数返回一个文件对象,相当于获取了操作文件的“手柄”。- 使用
with语句,进入时自动打开文件,离开时自动关闭,即使内部代码出错也会关闭,因为它利用了上下文管理器协议。 - 文本模式(
r/w)会自动处理编码(如 UTF-8),二进制模式(rb/wb)处理原始字节(如图片、模型文件)。 json和csv模块提供了更高级的结构化数据读写方式。
演示用例(带注释)
import json
import csv
# ---- 文本文件写入与读取 ----
with open("demo.txt", "w", encoding="utf-8") as f:
f.write("第一行内容\n")
with open("demo.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip())
# ---- 二进制文件读写(例如保存/加载图像或模型权重) ----
data = bytes([0, 128, 255]) # 模拟一些二进制数据
with open("data.bin", "wb") as f:
f.write(data)
with open("data.bin", "rb") as f:
content = f.read()
print("二进制内容:", list(content))
# ---- JSON 文件:解析大模型 API 响应常用 ----
response_json = '{"model": "gpt-4", "choices": [{"text": "你好"}]}'
parsed = json.loads(response_json)
print("解析 JSON:", parsed["model"])
with open("response.json", "w", encoding="utf-8") as f:
json.dump(parsed, f, ensure_ascii=False, indent=2)
# ---- CSV 文件:批量处理数据集的常用格式 ----
with open("scores.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["name", "score"])
writer.writeheader()
writer.writerow({"name": "model_a", "score": 0.89})
writer.writerow({"name": "model_b", "score": 0.93})
with open("scores.csv", "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
print(f"{row['name']}: {row['score']}")
输出结果
第一行内容
二进制内容: [0, 128, 255]
解析 JSON: gpt-4
model_a: 0.89
model_b: 0.93
AI 应用场景:模型权重、图片数据用二进制模式读写;API 返回结果用 JSON 解析;训练数据、评测结果常用 CSV 存储。这些是 AI 开发中读写数据的必备技能。
二、中级篇
1. 迭代器与生成器
问题
处理海量数据时,一次性把所有数据加载到内存太浪费,有没有一种“按需生产”的方式?
生活化类比
- 迭代器像是餐厅服务员,一次只给你上一道菜,吃完才能要下一道,而不是把整桌菜堆满。
- 生成器是智能厨师,只有你喊“上菜”(调用 next)时,他才会现做一道,并记住上次做到哪一步了。
原理
- 迭代器实现了两个关键动作:
__iter__(返回自己)和__next__(返回下一个元素)。 for循环背后就是反复调用next()直到StopIteration被抛出。- 生成器是用
yield代替return的函数。调用生成器函数不会立即运行代码,而是返回一个生成器对象。每次用next()请求时,代码执行到yield处暂停并吐出值,状态保留,下次从暂停点继续。 - 生成器表达式
(x for x in ...)是生成器的简写形式,比列表推导式更节省内存。
演示用例(带注释)
# ---- 生成器函数:斐波那契数列 ----
def fibonacci(n: int):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
for num in fibonacci(5):
print(num, end=" ")
print()
# ---- 生成器表达式(懒加载,不立即分配内存) ----
gen_exp = (x * 2 for x in range(1000000)) # 不会立刻产生 100 万个元素
# 只取出前 3 个
for i, val in enumerate(gen_exp):
if i >= 3:
break
print(val, end=" ")
print()
# ---- 自定义迭代器类:倒计时 ----
class CountDown:
def __init__(self, start: int):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < 0:
raise StopIteration
value = self.current
self.current -= 1
return value
for n in CountDown(2):
print(n, end=" ")
输出结果
0 1 1 2 3
0 2 4
2 1 0
AI 应用场景:大语言模型的流式输出(SSE)就是生成器的典型应用,模型一边生成 token 一边把内容“推”给客户端。生成器表达式常用于逐条读取大型数据集而不撑爆内存。
2. 装饰器
问题
你想给某个函数增加计时、日志等功能,但又不想修改函数本身的代码,怎么办?
生活化类比
装饰器就像给礼物包上包装纸:礼物本身(原函数)不变,外面多了一层漂亮的包装(额外功能)。你可以随时包上或拆掉,而不影响礼物。
原理
- 装饰器本质上是一个接收函数、返回新函数的“函数工厂”。
@decorator相当于执行原函数 = decorator(原函数)。- 内部通常定义
wrapper函数,在原函数调用前后添加操作(如计时),并利用闭包记住原函数。 - 使用
functools.wraps可保留原函数的名称等元信息,防止被覆盖。
装饰器让代码复用更优雅,遵循“开放扩展,封闭修改”原则。
演示用例(带注释)
import time
from functools import wraps
# 装饰器函数,接收被装饰的函数 func
def timer(func):
@wraps(func) # 保留 func 的元数据(如函数名)
def wrapper(*args, **kwargs):
start = time.perf_counter() # 开始计时
result = func(*args, **kwargs) # 执行原函数
end = time.perf_counter() # 结束计时
print(f"{func.__name__} 耗时:{end - start:.6f} 秒")
return result
return wrapper # 返回包装后的函数
# 使用 @timer 装饰器,相当于 slow_sum = timer(slow_sum)
@timer
def slow_sum(n: int) -> int:
total = 0
for i in range(n):
total += i
return total
print(slow_sum(100000)) # 调用时自动打印耗时
输出结果(耗时因机器而异)
slow_sum 耗时:0.004512 秒
4999950000
AI 应用场景:装饰器常用于 API 调用计时、自动重试(网络请求失败时)、缓存推理结果(避免重复调用模型)以及权限校验。
3. 上下文管理器
问题
有些资源(文件、数据库连接、锁)用完后必须释放,手动释放容易遗漏,如何自动管理?
生活化类比
上下文管理器就像自动感应水龙头:手伸过去(进入 with)自动出水,手离开(退出 with)自动关水,无需记得关,也不会浪费水。
原理
- 实现
__enter__(获取资源)和__exit__(释放资源)的对象称为上下文管理器。 with语句调用__enter__获取资源并赋值给as后的变量;即使在with块内发生异常,也会调用__exit__进行清理。- 也可用
@contextmanager装饰一个单次yield的生成器,yield前为进入操作,yield后为退出操作。
这种方式确保了资源安全性,代码也简洁明了。
演示用例(带注释)
from contextlib import contextmanager
# ---- 基于类的上下文管理器 ----
class ManagedFile:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode, encoding="utf-8")
print("文件已打开")
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
print("文件已关闭")
with ManagedFile("test.txt", "w") as f:
f.write("Hello Context Manager!")
# ---- 基于生成器的上下文管理器 ----
@contextmanager
def managed_list(lst: list):
print("进入上下文,备份列表")
backup = lst.copy()
try:
yield lst # 将列表交给 with 块内的变量
finally:
lst.clear()
lst.extend(backup)
print("退出上下文,恢复列表")
data = [1, 2, 3]
with managed_list(data) as working:
working.append(4)
print("内部修改:", working)
print("外部查看:", data)
输出结果
文件已打开
文件已关闭
进入上下文,备份列表
内部修改: [1, 2, 3, 4]
退出上下文,恢复列表
外部查看: [1, 2, 3]
AI 应用场景:在进行模型推理时临时切换到评估模式(
model.eval()),推理完成后恢复到训练模式;数据库事务的自动提交/回滚;分布式训练中获取和释放锁,都是上下文管理器的经典应用。
4. 异步编程(asyncio)
问题
程序要并发处理多个网络请求,但不想使用复杂的多线程,怎样高效地“同时”进行多件事?
生活化类比
你在家煮饭:把米下锅后,不用一直盯着,可以同时切菜;饭好了,回来盛饭,这期间没有雇佣多个厨师,只是利用等待时间做别的事。
异步编程就是一位大厨,利用空闲时间穿插做多道菜。
原理
asyncio基于事件循环,单线程轮询各个任务。async def定义协程,内部使用await挂起(让出控制权),当等待的操作(如网络 I/O、睡眠)完成时,事件循环会恢复执行。- 多个协程通过
asyncio.gather并发调度,看起来是同时进行的,实际上是在等待间隙切换任务。 - 如果需要在协程中调用同步阻塞代码(例如本地的同步模型推理),可以使用
loop.run_in_executor将阻塞操作放到线程池中执行,避免卡死事件循环。
演示用例(带注释)
import asyncio
import time
# 定义一个异步协程函数(模拟网络请求)
async def fetch_data(task_id: int, delay: float) -> str:
print(f"任务 {task_id} 开始,等待 {delay} 秒...")
await asyncio.sleep(delay) # 模拟 I/O 等待,释放控制权
print(f"任务 {task_id} 完成")
return f"数据来自任务{task_id}"
# 模拟一个同步的阻塞函数(例如本地模型推理)
def sync_inference(model_name: str) -> str:
time.sleep(1) # 模拟耗时计算
return f"{model_name} 推理结果"
async def main():
# 并发执行异步任务
tasks = [
fetch_data(1, 2.0),
fetch_data(2, 1.0),
fetch_data(3, 1.5),
]
results = await asyncio.gather(*tasks)
print("异步任务全部完成,结果:", results)
# 在协程中安全地调用同步阻塞代码
loop = asyncio.get_running_loop()
infer_result = await loop.run_in_executor(None, sync_inference, "resnet")
print("同步推理结果:", infer_result)
# 启动事件循环
asyncio.run(main())
输出结果
任务 1 开始,等待 2.0 秒...
任务 2 开始,等待 1.0 秒...
任务 3 开始,等待 1.5 秒...
任务 2 完成
任务 3 完成
任务 1 完成
异步任务全部完成,结果: ['数据来自任务1', '数据来自任务2', '数据来自任务3']
同步推理结果: resnet 推理结果
原因解释:任务 2 只等了 1 秒,所以最先完成;任务 3 等了 1.5 秒,第二个完成;任务 1 等了 2 秒,最后完成。并发执行的总耗时约等于最慢任务的时间(2 秒),而不是三个任务时间的总和。
AI 应用场景:同时调用多个模型 API、流式聊天、异步读取批量数据。当调用 scikit-learn 等同步库进行推理时,必须用
run_in_executor将阻塞操作扔进线程池,否则整个异步服务会卡死。另外,async with和async for可用于异步数据库客户端和流式 HTTP 响应,在实际工程中也很常见。
5. 类型注解
问题
代码多了以后,常常忘记某个函数参数该传什么类型,返回值又是什么类型,怎么办?
生活化类比
就像给药品包装写明成分和用法用量,虽然你不一定照着做,但看一眼就知道该吃什么、怎么吃。类型注解就是代码的“成分说明”。
原理
- Python 是动态语言,变量类型在运行时确定。类型注解(如
name: str)只是提示,不会在运行时强制检查。 - 注解信息保存在函数的
__annotations__属性里,可以被第三方工具(如 mypy)读取,在运行前进行静态分析,提前发现类型错误。 - 运行
mypy your_file.py即可扫描整个文件,提示类型不匹配的地方。 typing模块提供List、Dict、Optional等高级类型,能表达更复杂的结构。对于更精确的 API 响应结构,可以使用TypedDict来定义字典的“形状”。
演示用例(带注释)
from typing import List, Dict, Union, Optional
# ---- 基本注解 ----
def add(a: int, b: int) -> int:
"""参数和返回值都标注为 int 类型"""
return a + b
# ---- 复合类型定义 ----
UserDict = Dict[str, Union[str, int]] # 自定义类型别名
def get_user(id: int) -> Optional[UserDict]:
"""根据 id 返回用户信息,可能返回 None"""
if id == 1:
return {"name": "Alice", "age": 30}
return None
user = get_user(1)
if user:
print(user["name"], user["age"])
# ---- 泛型列表 ----
def average(numbers: List[float]) -> float:
"""计算浮点数列表的平均值"""
return sum(numbers) / len(numbers)
scores: List[float] = [85.5, 90.0, 76.5]
print(f"平均分:{average(scores):.1f}")
输出结果
Alice 30
平均分:84.0
AI 应用场景:类型注解配合 mypy 可以在代码上线前就发现类型不匹配的 bug。对于复杂的 API 返回数据(如 OpenAI 的响应 JSON),使用
TypedDict可以精确描述每个字段的类型和是否可选,让 IDE 自动补全和检查。
面试模拟题
1. 场景型:你在调用一个模型 API 时,发现返回的 JSON 中 choices[0].message.content 字段有时不存在。如何安全地提取这个字段而不让程序崩溃?
答案要点:使用 dict.get("choices", [{}])[0].get("message", {}).get("content", "") 链式安全取值,配合 try/except 捕获 KeyError 和 IndexError,并打印完整 traceback 方便定位是 API 返回异常还是网络问题。
2. 原理型:为什么在函数中写 def load_model(cache={}) 是有风险的?正确的写法是什么?
答案要点:Python 的默认参数只在函数定义时求值一次,可变对象 {} 会被多次调用共享,导致缓存互相污染。正确写法是 def load_model(cache=None): if cache is None: cache = {}。
3. 场景型:你有一个 10GB 的日志文件,每行是一个 JSON,需要提取特定字段做分析。如何在不撑爆内存的情况下处理?
答案要点:使用生成器逐行读取,for line in file 每次只读一行到内存,yield json.loads(line) 逐条产出记录。结合 chunksize 分块读取如果是 CSV 格式。
4. 对比型:asyncio.gather 和 loop.run_in_executor 分别在什么 AI 应用场景下使用?
答案要点:gather 用于并发调用多个异步 API(如同时请求三个模型),利用等待 I/O 的间隙切换。run_in_executor 用于在协程中调用同步阻塞代码(如 scikit-learn 的 model.predict()),将其放入线程池避免阻塞事件循环。
总结
这篇博客用“问题-类比-原理-演示”的四步法,并针对 AI 应用开发补充了进阶操作和实战提示。从基本的 get、break、super()、多重异常,到文件 I/O 的二进制与结构化读写,再到生成器表达式、run_in_executor、mypy 静态检查——你现在不仅掌握了 Python 核心语法,还理解了它们在真实 AI 开发中的位置。亲手运行每一段代码,把这些基础刻进肌肉记忆里,它们会成为你构建更复杂系统的坚实基石。
更多推荐

所有评论(0)