D3 学习笔记:AI 工程师的 Python 基础速通
系列:海口三港 AI 智慧管控平台 · 从参赛大屏到 AI 全栈
仓库:https://github.com/2003Tim/haikou-ai-port
前言:为什么我重新学 Python
我本科学过 Python,以为自己会用就够了。直到决定转 AI 应用工程师 方向,看了 FastAPI 文档才发现 —— Optional、@dataclass、async 这些语法不是"进阶",而是日常。
所以花了 1 天,把 AI 项目里最常用的 6 个 Python 模式过了一遍,记下来。
如果你也是前端会一些、Python 想补基础的 AI 方向同学,这篇可能适合你。
一、类型注解:让代码"自带文档"
def greet(name: str, greeting: str = "你好") -> str:
return f"{greeting}, {name}!"
写法很简单:
- 参数 后面加
: 类型(比如name: str) - 返回值 前面加
-> 类型(比如-> str) - 默认值 写在类型注解后面(
greeting: str = "你好")
IDE 会根据类型注解自动补全和报错,而 FastAPI 看到类型注解会自动生成 Swagger 文档 —— 前端不用问后端就能知道接口返回什么。
💡 一个真实的对比:
# 没有类型注解 —— FastAPI 不知道返回啥,Swagger 只能写 "string" def get_port(port_id): ... # 有类型注解 —— FastAPI 自动知道返回 Port 对象 def get_port(port_id: int) -> Port: ...
二、Optional:别让你的程序"猜 Null"
from typing import Optional
def find_port(port_id: int) -> Optional[dict]:
ports = {1: {"name": "秀英港"}}
return ports.get(port_id) # 不存在就返回 None
Optional[X] 等价于 Union[X, None],意思是"可能是 X,也可能是 None"。
看到 Optional,你的代码里必须做 None 检查 —— 否则前端传一个数据库里没有的 ID,后端会直接崩。
port = find_port(999)
if port is not None: # 注意:必须用 is not None,不是 != None
print(port["name"])
else:
print("港口不存在")
⚠️ 新人坑:
None比较要用is,不要用==。这是 Python 的风格规范(PEP 8),也是正确做法。
三、@dataclass:写数据类的最短方式
写一个港口类,普通写法要写一堆样板代码:
# 传统写法
class Port:
def __init__(self, id, name, location):
self.id = id
self.name = name
self.location = location
def __repr__(self):
return f"Port(id={self.id}, name={self.name})"
用 @dataclass,一行搞定:
from dataclasses import dataclass
@dataclass
class Port:
id: int
name: str
location: str
capacity: int = 0 # 默认值要放在最后
@dataclass 自动帮你生成:
__init__(初始化)__repr__(调试输出)__eq__(两个相同 dataclass 可以直接==比较)
💡 后续影响:SQLAlchemy 模型、Pydantic 数据模型都是基于 dataclass 思路设计的。今天学会这个,以后学数据库建模零障碍。
四、List / Dict 复杂类型
容器类型也可以标注,告诉别人里面装的是什么:
from typing import List, Dict
def get_all_ports() -> List[Port]:
return [Port(1, "秀英港", "海口"), Port(2, "新海港", "海口")]
def count_by_location(ports: List[Port]) -> Dict[str, int]:
result = {}
for p in ports:
result[p.location] = result.get(p.location, 0) + 1
return result
List[X]= 列表里每个元素都是 XDict[K, V]= 键是 K,值是 V
FastAPI 看到这些会自动生成 Swagger 示例,前端在文档里直接看到 JSON 格式,不用反复问"这个字段返回什么"。
五、列表推导式:Pythonic 的写法
过滤港口列表的两种写法:
# ❌ 不 Pythonic
result = []
for p in ports:
if p.capacity >= 6000:
result.append(p.name)
# ✅ Pythonic
result = [p.name for p in ports if p.capacity >= 6000]
一行代替三行,而且性能更好(底层是 C 实现的)。
💡 真实战场:ML 项目里到处都在用 —— 清洗数据、过滤特征、批量转换格式。不会写这个,代码会显得"很 Java"。
进阶用法:
# 字典推导式
{name: capacity for name, capacity in [("秀英港", 6400), ("新海港", 8000)]}
# {'秀英港': 6400, '新海港': 8000}
# 嵌套推导式
matrix = [[i*j for j in range(3)] for i in range(3)]
# [[0, 0, 0], [0, 1, 2], [0, 2, 4]]
六、if name == "main" 入口保护
def greet(name):
return f"Hi, {name}"
if __name__ == "__main__":
print(greet("World"))
这条 if 看着奇怪,但作用很重要:
- 直接跑这个文件:
python learn_d3.py→ 执行 if 块里的代码 - 被其他文件 import:
from learn_d3 import greet→ 不会 执行 if 块
不加这个保护,你的模块在被 import 时会意外跑测试代码或打印一堆调试信息,这是经典踩坑场景。
我踩过的坑(写给未来的自己)
-
虚拟环境每次新开 PowerShell 都要重新激活
第一次创建虚拟环境后,我以为一劳永逸。结果第二天打开 PowerShell,提示符前面没有(backend),直接pip install装到了系统 Python。现在养成了习惯:进项目目录先看提示符。 -
uv 自动用 Python 3.13.13,而不是系统装的 3.12.10
一开始我还以为是 bug,后来才搞明白:uv 自带 Python 版本管理,会自动下载并用合适的版本。如果某天项目要锁死 3.11,只需uv python pin 3.11。 -
写错了
Remove-Item hello.py也没事
uv 0.12 在 Windows 上默认不创建 hello.py,我按教程去删,反而报错。后来想通:报错比静默成功更安全,起码知道哪里不对。 -
None 比较要用
is,不要!=
这是 PEP 8 规范,正确写法是if x is not None,不是if x != None。虽然!= None也能跑,但风格不对,代码审查会被打回。
完整代码
👉 GitHub: learn_d3.py
直接 clone 仓库跑就行:
git clone https://github.com/2003Tim/haikou-ai-port.git
cd haikou-ai-port/backend
uv venv
.\.venv\Scripts\Activate.ps1
python learn_d3.py
下一步:D4 学什么?
D4 我会学 装饰器、生成器、async/await —— 这三个是 FastAPI 的底层机制。理解了之后看 FastAPI 源码不会一脸懵,也能自己写装饰器做权限校验、日志记录。
如果你也在学,可以在评论区一起交流 ~
参考资料
- Python 官方文档:https://docs.python.org/3/
- Real Python:https://realpython.com/
- 《流畅的 Python》(后续会反复翻的书)

浙公网安备 33010602011771号