我的全栈学习之旅:FastAPI (持续更新!!!)

1.FastAPI是什么?

FastAPI 是一个基于Python 3.6+Starlette (Web 部分)+Pydantic (数据验证部分)构建的现代、快速的 Web 框架 ,主要用于开发 API(尤其是 RESTful API)。它的名字就体现了两个特点:Fast (性能高效)和API(专注接口开发)。

不熟悉这一块的我发现几个不了解的名词:Web框架,Starlette,Pydantic(包括数据验证),API(包括 RESTful API)。下面,我们分别简短介绍一下这几个名词:

1.1Web框架

1.1.1什么是Web框架?

Web 框架(Web Framework) = 一组工具和库,用来帮助开发者更高效地构建 Web 应用或 API。

Web 应用的本质是:

客户端(浏览器、App、其他服务) → 发送 HTTP 请求 → 服务器 → 返回 HTTP 响应。

而 Web 框架就是帮你处理这些请求与响应的底层细节,让你不用从零开始写"HTTP 协议解析器"。

如果没有框架,你得自己写类似这样的东西:

复制代码
import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("127.0.0.1", 8080))
server_socket.listen(5)

while True:
    client_socket, addr = server_socket.accept()
    request = client_socket.recv(1024).decode("utf-8")
    response = "HTTP/1.1 200 OK\\n\\nHello World"
    client_socket.send(response.encode("utf-8"))
    client_socket.close()

这就是一个"手写服务器"。它能跑,但:

  • 你要自己解析 HTTP 协议(GET/POST/Headers/Body)
  • 你要自己写路由分发逻辑
  • 你要自己处理 JSON、Cookies、Session、模板、数据库交互

太原始了。

1.1.2为什么需要 Web 框架?

Web 框架出现就是为了解决 重复劳动复杂性 问题。

它们帮我们解决了这些事:

  1. 路由分发 :不同 URL → 不同函数,比如 /users → 用户接口。
  2. 请求解析:自动解析 HTTP 请求(Query、Body、Headers、Cookies)。
  3. 响应构建:自动构造 HTTP 响应,支持 JSON/HTML/File 等。
  4. 中间件机制:可以在请求/响应前后插入逻辑(日志、认证、安全检查)。
  5. 开发效率提升:很多常见功能直接内置(模板渲染、ORM、验证等)。
  6. 生态和规范:框架通常有庞大社区,提供插件、扩展和最佳实践。

换句话说:

即,Web 框架把底层重复的、易错的工作抽象出来,让开发者专注于业务逻辑。

1.2 Starlette

1.2.1什么是Starlette?

Starlette 是一个基于 Python 的 轻量级 ASGI Web 框架/工具包

  • ASGI (Asynchronous Server Gateway Interface)是 WSGI 的升级版,专门为 异步支持(async/await)而设计。
  • Starlette 提供了一套构建 Web 应用的核心能力:路由、请求/响应处理、中间件、WebSocket、后台任务等。

它本身是一个 极简的框架,功能够用,但不会捆绑一大堆东西。

可以理解为:Starlette 是 FastAPI 的地基

1.2.2为什么需要Starlette?

在 FastAPI 出现之前,Python 的主流 Web 框架(Flask/Django)是基于 WSGI 的,而 WSGI 是 同步模型,在高并发或需要 WebSocket 的场景下效率不高。

于是 Starlette 出现了:

  • 支持 ASGI → 异步请求处理,性能更好
  • 提供一套 现代化的 Web 框架工具集 → 不用从零写

1.2.3什么是路由?

在 Web 应用中,路由就是把 URL(路径)和对应的处理函数关联起来的规则

简单来说:

客户端访问哪个 URL,服务器就调用哪个函数来处理请求

比如:

  • 用户访问 http://example.com/home → 返回首页内容
  • 用户访问 http://example.com/users/123 → 返回 ID=123 的用户数据

1.2.4 为什么需要路由?

想象一下,如果没有路由,你得自己写代码解析用户请求的 URL,然后用 if/else 去判断调用哪个函数,非常麻烦:

复制代码
if path == "/home":
    homepage()
elif path.startswith("/users/"):
    get_user()
else:
    not_found()

而有了 路由系统,你只需要声明规则,框架会自动帮你匹配。

1.3 Pydantic

1.3.1 什么是 Pydantic?

Pydantic 是一个 Python 的数据验证和数据解析库

它的核心作用是:

根据 Python 类型注解(type hints)来验证数据,并自动转换成正确的类型。

也就是说,你只需要用 Python 的类型系统(str, int, list, dict, ...)定义数据结构,Pydantic 就能帮你:

  • 解析输入数据(比如 JSON)
  • 验证数据合法性(比如 age 必须是整数,email 必须是邮箱格式)
  • 自动转换类型 (比如 "123" 自动转成 int 123

1.3.2 为什么需要 Pydantic?

在 Web 应用中,我们经常需要处理用户输入:

  • 表单提交
  • JSON 请求体
  • URL 参数

这些输入往往"不靠谱",可能类型不对、字段缺失、格式错误。

如果不用 Pydantic,我们得手动写验证逻辑,比如:

复制代码
def create_user(data: dict):
    if "name" not in data or not isinstance(data["name"], str):
        raise ValueError("Invalid name")
    if "age" not in data or not isinstance(data["age"], int):
        raise ValueError("Invalid age")
    return {"name": data["name"], "age": data["age"]}

很啰嗦,而且容易漏掉。

有了 Pydantic,这些验证可以通过声明式模型自动完成。

1.3.3 Pydantic 怎么用?

Pydantic 提供了一个基类 BaseModel,你可以继承它来定义数据模型:

复制代码
from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

使用:

复制代码
user = User(name="Alice", age="20")
print(user)
# name='Alice' age=20   (字符串 "20" 自动转成 int)

如果数据不合法:

复制代码
user = User(name="Alice", age="abc")
# 会抛出 ValidationError: value is not a valid integer

1.3.4 Pydantic 的功能

  1. 数据验证(Validation)
    • 类型验证(int/str/float/list/dict...)
    • 格式验证(邮箱、URL、UUID...)
    • 范围验证(长度、最大值、最小值)
  2. 数据解析(Parsing)
    • 自动转换类型,比如 "123"123
    • 支持复杂嵌套结构
  3. 数据序列化(Serialization)
    • 可以把模型转成 JSON / dict,方便返回 API 响应
  4. 文档生成(Schema)
    • Pydantic 可以自动生成 JSON Schema
    • FastAPI 利用这个特性自动生成 API 文档

1.3.5 FastAPI 为什么用 Pydantic?

FastAPI 的一个核心设计是:

所有请求体 / 查询参数 / 响应体 都用 Pydantic 模型来定义

例子:

复制代码
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    name: str
    age: int

@app.post("/users/")
def create_user(user: User):
    return {"msg": f"Hello {user.name}, you are {user.age} years old"}

流程:

  1. 客户端发送请求 POST /users {"name": "Tom", "age": "30"}
  2. FastAPI 用 User 模型解析 JSON
    • 检查字段是否完整
    • 验证 age 是否是整数
    • 自动把 "30" 转换成 30
  3. 如果验证失败,返回 422 错误 + 错误提示
  4. 如果验证成功,传递一个 User 实例给 create_user 函数

1.4 API

API(Application Programming Interface,应用程序编程接口)是一套规则和协议,定义了不同软件应用程序之间如何相互通信和交互。简单来说,API就像是软件之间的"桥梁"或"翻译器"。

1.4.1 API的核心概念

API规定了:

  • 可以发出什么样的请求
  • 如何发出这些请求
  • 使用什么数据格式
  • 遵循什么约定

就像餐厅的菜单一样,API告诉你可以"点什么菜"(可用功能)以及"怎么点菜"(调用方式)。

1.4.2 RESTful API详解

REST(Representational State Transfer,表现状态转移)是一种架构风格,RESTful API是遵循REST原则设计的API。

REST的核心原则:

1. 无状态性(Stateless)

  • 每个请求都包含处理该请求所需的所有信息
  • 服务器不保存客户端的状态信息

2. 统一接口(Uniform Interface)

  • 使用标准的HTTP方法(GET、POST、PUT、DELETE等)
  • 资源通过URL来标识
  • 使用标准的HTTP状态码

3. 客户端-服务器架构

  • 明确分离用户界面和数据存储
  • 提高了可移植性和可扩展性

4. 可缓存性(Cacheable)

  • 响应数据可以被标记为可缓存或不可缓存

RESTful API的常见操作:

复制代码
GET /api/users          # 获取所有用户
GET /api/users/123      # 获取ID为123的用户
POST /api/users         # 创建新用户
PUT /api/users/123      # 更新ID为123的用户
DELETE /api/users/123   # 删除ID为123的用户

1.4.3 API的实际应用场景

  • 移动应用:手机App通过API获取服务器数据
  • 网站集成:网站嵌入地图、支付、社交媒体功能
  • 微服务架构:不同服务模块之间的通信
  • 第三方服务:如天气API、翻译API、支付API等

1.4.4 优势

  • 模块化:不同系统可以独立开发和维护
  • 重用性:一个API可以被多个应用程序使用
  • 灵活性:可以更换底层实现而不影响使用者
  • 标准化:RESTful API提供了统一的交互方式

API本质上让不同的软件能够"对话",而RESTful API则提供了一套标准化的"对话规则",使得这种交流更加高效和可预测。

2.为什么需要FastAPI(FastAPI是解决什么问题?)

在 FastAPI 出现之前,Python 主流的 Web 框架主要是:

  • Flask :轻量、灵活,但缺少内置的验证、文档生成功能,开发大型项目需要写很多样板代码
  • Django :功能齐全,但更适合做"全栈"应用(数据库 + 模板渲染 + 管理后台),对单纯的 API 项目来说显得笨重。
  • Tornado:异步框架,适合高并发,但开发体验不如 Flask 友好。

这些框架在 开发现代 API 时往往存在几个痛点:

(1)数据验证繁琐:需要手动解析和验证请求体参数(比如 JSON),容易遗漏。

(2)文档缺失:接口文档通常需要单独维护,很难和代码保持同步。

(3)性能问题:传统的同步框架在高并发下效率较低。

(4)异步支持不友好:Python 的 async/await 出现后,很多旧框架集成不够顺畅。

3.FastAPI是如何解决这些问题的?

FastAPI 的设计理念就是:

高性能 + 开发效率 + 自动化文档 + 类型安全

具体来说:

(1) 类型提示驱动

FastAPI 深度利用了 Python 的 类型注解(type hints)

复制代码
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    name: str
    age: int

@app.post("/users/")
def create_user(user: User):
    return {"msg": f"Hello {user.name}, you are {user.age} years old"}
  • 这里 User 模型由 Pydantic 自动做验证,保证请求体符合要求。
  • 如果客户端发错类型(比如 age 传了 "abc"),FastAPI 会自动返回 422 错误。

(2) 自动生成文档

启动服务后,可以直接访问:

  • http://127.0.0.1:8000/docs → Swagger UI

  • http://127.0.0.1:8000/redoc → ReDoc

    文档完全根据代码自动生成,不需要额外维护。

(3) 异步性能

FastAPI 基于 Starlette ,天然支持 async/await ,性能接近 Node.jsGo 的 Web 框架。

(4) 开发效率

  • 自动完成 数据解析 → 验证 → 错误提示
  • 自动生成 交互式文档
  • 代码量少,清晰可维护

4.FastAPI的优缺点

和其他方案相比,FastAPI的优缺点如下:

4.1 FastAPI的优点

  • 高性能:基于 Starlette,性能在 Python 框架中名列前茅。
  • 开发效率高:几乎"零配置",类型注解直接变成参数验证规则。
  • 文档自动化:自带 Swagger + ReDoc,不需要额外写 API 文档。
  • 类型安全:利用 Python typing 和 Pydantic,减少运行时错误。
  • 异步支持:适合高并发场景(爬虫接口、实时数据服务)。
  • 社区发展快:近几年在后端开发者中非常流行。

4.2 FastAPI的缺点

  • 生态较新:相比 Django/Flask,周边库、教程和案例还没那么丰富。
  • 学习曲线:需要熟悉 Python 类型注解和 Pydantic,否则会觉得陌生。
  • 全栈能力不足:不像 Django 自带 ORM、Admin 等,做 Web 全栈项目需要自己拼接其他库(比如 SQLAlchemy、Jinja2)。
  • 极端高性能不如 Go/Node:虽然在 Python 里算快,但和 Go、Rust 等语言比还是有差距。

5.一些概念

5.1 FastAPI的handler

FastAPI 里,所谓的 handler 一般就是指 路由处理函数(endpoint function)

当客户端(比如浏览器或其他服务)发起一个 HTTP 请求时,FastAPI 会根据请求路径和方法,把请求分发给对应的 handler 来处理,然后返回响应。

举个例子

复制代码
from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
async def say_hello():
    return {"message": "Hello, world!"}

这里的 say_hello 就是一个 handler ,它对应一个 GET /hello 的请求。

  • 当有人访问 http://127.0.0.1:8000/hello 时,FastAPI 会调用 say_hello() 这个 handler。
  • handler 可以是 async def(异步函数),也可以是 def(同步函数)。
  • handler 的返回值会被 FastAPI 自动转换成 HTTP Response

handler 的几个特点

  • 路由绑定 :通过装饰器 @app.get(), @app.post(), @app.put() 等绑定到具体的路径和 HTTP 方法(路径就是上面例子中的"/hello")。
  • 参数处理:FastAPI 会自动把 URL 参数、查询参数、请求体解析出来,直接传给 handler 的函数参数。
  • 异步执行 :推荐用 async def,这样 FastAPI 可以利用 asyncio 提升并发性能。

5.2事件循环event loop

理解事件循环event loop这个概念是理解FastAPI这类Python异步编程的核心。

5.2.1 事件循环是什么?

事件循环 是一个不停"转圈"的调度器,它的职责是:

  • 管理所有的 异步任务(coroutines / futures / tasks)
  • 监控 I/O 状态(比如网络、文件、socket)
  • 决定什么时候让哪个任务继续执行,什么时候挂起等待(调度器的职责

换句话说:

事件循环是异步程序的大脑,负责协调多个任务在同一个线程里并发执行。

5.2.2 为什么需要事件循环?

传统的同步代码是这样的:

复制代码
def task1():
    time.sleep(3)  # 阻塞3秒
    print("task1 done")

def task2():
    time.sleep(2)  # 阻塞2秒
    print("task2 done")

task1()
task2()

执行时间 = 3 + 2 = 5秒 。因为 sleep 阻塞了整个线程。

异步方式则不同:

复制代码
import asyncio

async def task1():
    await asyncio.sleep(3)
    print("task1 done")

async def task2():
    await asyncio.sleep(2)
    print("task2 done")

async def main():
    await asyncio.gather(task1(), task2())

asyncio.run(main())

这里 asyncio.sleep 并不会阻塞线程,而是把"等待 3 秒钟"交给 事件循环 管理。

事件循环会在这 3 秒里继续跑别的任务,所以最终耗时只有 3秒(而不是 5 秒)。

5.2.3 事件循环是怎么工作的?

可以类比成电影院的放映员:

  • 观众(任务)坐在位置上等待。
  • 放映员(事件循环)会巡视:
    • "你准备好了吗?"
    • 如果某个观众(任务)需要等待(比如 I/O 还没完成),就先跳过。
    • 如果准备好了,就让他执行一小段(await 之前的代码)。
  • 不停循环,直到所有任务都完成。

所以它本质上是:单线程里轮流调度多个任务,让它们看起来像"并发"。

5.2.4 Python 里的事件循环

在 Python asyncio 里:

  • asyncio.run(main()) 会创建并启动一个事件循环。
  • await 会把控制权交回给事件循环。
  • 事件循环会不断运行,直到所有任务完成。

也可以手动操作:

复制代码
import asyncio

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

5.2.5 常见误区

  • 事件循环通常 只能有一个(每个线程一个),否则会乱套。

  • 它不是并行(parallel),只是并发(concurrent)------真正的并行要用多线程/多进程。

  • 如果在事件循环里再用 asyncio.run()(相当于再开一个 loop),就会报你之前那个错:

    复制代码
    RuntimeError: asyncio.run() cannot be called from a running event loop

6.几个Demo

需要安装的模块:

复制代码
pip install fastapi uvicorn "python-jose[cryptography]" passlib[bcrypt]

6.1 Demo1

学习目标:理解 FastAPI 的基本结构、路由、请求方法

功能点

  • 创建一个 FastAPI 应用
  • 定义一个 GET 路由返回 JSON
  • 定义一个 POST 路由接收参数并返回

示例代码

复制代码
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# 定义请求体数据模型
class Item(BaseModel):
    name: str
    price: float

@app.get("/")
def read_root():
    return {"message": "Hello, FastAPI!"}

@app.get("/hello/{name}")
def read_item(name: str):
    return {"greeting": f"Hello, {name}"}

@app.post("/items/")
def create_item(item: Item):
    return {"item": item}

💡 学会使用:

  • 路由 (@app.get, @app.post)
  • 路径参数 (/hello/{name})
  • 请求体 + Pydantic 模型

启动流程

  1. 把代码保存为 demo1.py

  2. 运行:

    复制代码
    uvicorn demo1:app --reload
    
    注意:
    如果报了"[WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。",则可以考虑换一个port口打开,如下面的指令:
    uvicorn demo1:app --reload --port 8001
    • demo1 是文件名(不带 .py
    • app 是 FastAPI 实例对象
    • -reload 代表热更新,修改代码不用重启

测试方法

  • 打开浏览器访问:

    • http://127.0.0.1:8000/ → 返回 {"message": "Hello, FastAPI!"}
    • http://127.0.0.1:8000/hello/Jerry → 返回 {"greeting": "Hello, Jerry"}
  • 使用 Swagger UI

    • 访问 http://127.0.0.1:8000/docs,你可以直接点按钮测试 API
  • curl 测试 POST:

    复制代码
    curl -X POST <http://127.0.0.1:8000/items/> \\
         -H "Content-Type: application/json" \\
         -d '{"name":"apple","price":3.5}'

6.2 Demo2

学习目标:掌握 CRUD、依赖注入、状态存储

功能点

  • 内存列表 模拟数据库
  • 实现 CRUD(增删改查)
  • 使用 QueryPathBody 参数
  • 引入依赖函数(例如检查 item 是否存在)

示例代码

复制代码
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel

app = FastAPI()

class Todo(BaseModel):
    id: int
    title: str
    completed: bool = False

todos = []

def get_todo(todo_id: int):
    for todo in todos:
        if todo.id == todo_id:
            return todo
    raise HTTPException(status_code=404, detail="Todo not found")

@app.post("/todos/", response_model=Todo)
def create_todo(todo: Todo):
    todos.append(todo)
    return todo

@app.get("/todos/", response_model=list[Todo])
def list_todos():
    return todos

@app.get("/todos/{todo_id}", response_model=Todo)
def read_todo(todo: Todo = Depends(get_todo)):
    return todo

@app.put("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: int, updated: Todo):
    for i, todo in enumerate(todos):
        if todo.id == todo_id:
            todos[i] = updated
            return updated
    raise HTTPException(status_code=404, detail="Todo not found")

@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):
    for i, todo in enumerate(todos):
        if todo.id == todo_id:
            todos.pop(i)
            return {"message": "Deleted successfully"}
    raise HTTPException(status_code=404, detail="Todo not found")

💡 学会使用:

  • CRUD API 设计
  • 依赖注入 (Depends)
  • 错误处理(HTTPException
  • 返回数据模型(response_model

启动流程

  1. 保存为 demo2.py

  2. 启动:

    复制代码
    uvicorn demo2:app --reload

测试方法

  • 创建任务:

    复制代码
    curl -X POST <http://127.0.0.1:8000/todos/> \\
         -H "Content-Type: application/json" \\
         -d '{"id":1,"title":"Learn FastAPI","completed":false}'
  • 查看所有任务:

    复制代码
    curl <http://127.0.0.1:8000/todos/>
  • 查看单个任务:

    复制代码
    curl <http://127.0.0.1:8000/todos/1>
  • 更新任务:

    复制代码
    curl -X PUT <http://127.0.0.1:8000/todos/1> \\
         -H "Content-Type: application/json" \\
         -d '{"id":1,"title":"Learn FastAPI deeply","completed":true}'
  • 删除任务:

    复制代码
    curl -X DELETE <http://127.0.0.1:8000/todos/1>

👉 同时你也可以去 http://127.0.0.1:8000/docs 直接点点点操作,方便很多。

6.3 Demo3

学习目标:掌握 认证、依赖注入、响应模型、JWT

功能点

  • 用户注册 & 登录
  • 密码哈希存储
  • 使用 JWT 进行登录认证
  • 保护需要权限的接口

示例代码

复制代码
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from passlib.context import CryptContext
import jwt
from datetime import datetime, timedelta

app = FastAPI()

# 密码加密工具
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = "mysecret"
ALGORITHM = "HS256"

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

# 模拟数据库
fake_users = {}

class User(BaseModel):
    username: str
    password: str

def authenticate_user(username: str, password: str):
    user = fake_users.get(username)
    if not user or not pwd_context.verify(password, user["hashed_password"]):
        return None
    return username

def create_access_token(username: str):
    expire = datetime.utcnow() + timedelta(minutes=30)
    return jwt.encode({"sub": username, "exp": expire}, SECRET_KEY, algorithm=ALGORITHM)

@app.post("/register")
def register(user: User):
    if user.username in fake_users:
        raise HTTPException(status_code=400, detail="User already exists")
    fake_users[user.username] = {"hashed_password": pwd_context.hash(user.password)}
    return {"msg": "User registered successfully"}

@app.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    username = authenticate_user(form_data.username, form_data.password)
    if not username:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    token = create_access_token(username)
    return {"access_token": token, "token_type": "bearer"}

@app.get("/protected")
def protected(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")
    return {"msg": f"Hello {username}, you accessed a protected route!"}

💡 学会使用:

  • 用户注册 / 登录
  • OAuth2PasswordBearer + Depends
  • 密码加密 (passlib)
  • JWT 认证 & 保护路由

启动流程

  1. 保存为 demo3.py

  2. 启动:

    复制代码
    uvicorn demo3:app --reload

测试方法

  • 注册新用户

    复制代码
    curl -X POST <http://127.0.0.1:8000/register> \\
         -H "Content-Type: application/json" \\
         -d '{"username":"jerry","password":"123456"}'
  • 登录获取 Token

    复制代码
    curl -X POST <http://127.0.0.1:8000/login> \\
         -H "Content-Type: application/x-www-form-urlencoded" \\
         -d "username=jerry&password=123456"

    你会得到:

    复制代码
    {
      "access_token": "xxxxx.yyyyy.zzzzz",
      "token_type": "bearer"
    }
  • 访问保护接口

    复制代码
    curl -X GET <http://127.0.0.1:8000/protected> \\
         -H "Authorization: Bearer xxxxx.yyyyy.zzzzz"

    成功的话返回:

    复制代码
    {"msg": "Hello jerry, you accessed a protected route!"}

👉 也可以直接去 http://127.0.0.1:8000/docs

  1. Authorize 按钮
  2. 输入 "Bearer <你的token>"
  3. 测试 /protected 接口,验证权限控制
相关推荐
用户3721574261353 小时前
Python 高效实现 Excel 与 CSV 互转:用自动化提升效率
python
The_Killer.3 小时前
近世代数(抽象代数)详细笔记--环(也有域的相关内容)
笔记·学习·抽象代数·
bcbnb3 小时前
使用BurpSuite进行网页和APP抓包的完整指南
后端
Starriers3 小时前
AI - Java AI - LangChain4J 实战
人工智能·后端
语落心生3 小时前
GPU负载方式详解
后端
用户51681661458413 小时前
使用全能电子地图下载器MapTileDownloader 制作瓦片图层详细过程
前端·后端
NickBi3 小时前
龙芯 LoongArch64编译es7.17.20
后端·elasticsearch
掘金者阿豪3 小时前
金仓数据库KingbaseES与MyBatis-Plus整合实践:电商系统开发实战
java·后端
ONE_Gua3 小时前
Wireshark常用过滤规则
前端·后端·数据挖掘