D3 学习笔记:AI 工程师的 Python 基础速通

系列:海口三港 AI 智慧管控平台 · 从参赛大屏到 AI 全栈
仓库:https://github.com/2003Tim/haikou-ai-port


前言:为什么我重新学 Python

我本科学过 Python,以为自己会用就够了。直到决定转 AI 应用工程师 方向,看了 FastAPI 文档才发现 —— Optional@dataclassasync 这些语法不是"进阶",而是日常

所以花了 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] = 列表里每个元素都是 X
  • Dict[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 时会意外跑测试代码或打印一堆调试信息,这是经典踩坑场景。


我踩过的坑(写给未来的自己)

  1. 虚拟环境每次新开 PowerShell 都要重新激活
    第一次创建虚拟环境后,我以为一劳永逸。结果第二天打开 PowerShell,提示符前面没有 (backend),直接 pip install 装到了系统 Python。现在养成了习惯:进项目目录先看提示符

  2. uv 自动用 Python 3.13.13,而不是系统装的 3.12.10
    一开始我还以为是 bug,后来才搞明白:uv 自带 Python 版本管理,会自动下载并用合适的版本。如果某天项目要锁死 3.11,只需 uv python pin 3.11

  3. 写错了 Remove-Item hello.py 也没事
    uv 0.12 在 Windows 上默认不创建 hello.py,我按教程去删,反而报错。后来想通:报错比静默成功更安全,起码知道哪里不对。

  4. 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 源码不会一脸懵,也能自己写装饰器做权限校验、日志记录。

如果你也在学,可以在评论区一起交流 ~


参考资料

posted @ 2026-08-21 17:14  吴逸清  阅读(3)  评论(0)    收藏  举报