当前位置:首页>python>从数据可视化到AI应用开发:Python全栈实战之旅

从数据可视化到AI应用开发:Python全栈实战之旅

  • 2026-10-11 05:43:11
从数据可视化到AI应用开发:Python全栈实战之旅

前言

在当今的技术世界中,Python以其简洁优雅的语法和强大的生态系统,成为了数据科学和Web开发领域的中流砥柱。作为一名Python开发者,你是否曾经想过:如何将数据分析的能力与Web应用开发相结合,打造出既有数据洞察力又具备交互性的智能应用?

本文将带你完成一场从数据分析可视化到AI智能应用开发的完整技术之旅。我们将从TMDB-TOP300电影榜单的数据分析入手,逐步深入到使用FastAPI构建"汉字迷盒"——一个基于DeepSeek大语言模型的智能字谜互动游戏。


数据分析案例

  1. 准备工作:导入依赖库、配置运行时参数、创建子图完成基本布局、加载数据
  2. 统计TOP300的电影中,每一年上映的电影数量的变化。(折线图)
  3. 统计对比不同语言电影数量。(柱状图)
  4. 统计对比不同类型电影数量。(柱状图)
  5. 统计对比各个电影评分的比例。(饼状图)

准备工作

import pandas as pdimport matplotlib.pyplot as pltfrom matplotlib.axes import Axes#展示中文plt.rcParams['font.sans-serif'] = ['SimHei']# 创建子图fig,axes = plt.subplots(nrows=2,ncols=2,figsize=(20,12),dpi = 100)fig.suptitle('TMDB-TOP300电影榜单数据统计',fontsize=23,x=0.5,y=0.95)#获取子图axes1:Axes = axes[0][0]axes2:Axes = axes[0][1]axes3:Axes = axes[1][0]axes4:Axes = axes[1][1]#加载数据data = pd.read_csv('../data/movies.csv',usecols=['电影名','年份','上映时间','类型','时长','评分','语言'],dtype={'年份':'Int64'})
① 创建一个 2×2 的画布            │            ▼② 给整个画布添加标题            │            ▼③ 将四个子图分别命名为   axes1、axes2、axes3、axes4            │            ▼④ 从 movies.csv 中读取需要的电影数据            │            ▼⑤ 后续可分别在四个子图中绘制不同统计图   (柱状图、折线图、饼图、散点图等)

统计TOP300的电影中,每一年上映的电影数量的变化。(折线图)

# 统计TOP300的电影中,每一年上映的电影数量的变化。(折线图)# 缺失值、异常值处理data['年份'] = data['年份'].fillna(data['上映时间'].str[:4])#分组统计yeart_count =  data.groupby('年份')['年份'].count()# 组装数据# x轴数据min_yaer = yeart_count.index.min()max_year = yeart_count.index.max()x = [ i for i in range(min_yaer,max_year + 1)]# y轴数据y = [int(yeart_count.get(i, 0)) for i in x]# 绘制折线图axes1.plot(x,y,color='green')axes1.set_title("每年电影数量变化折线图",fontsize=18)axes1.set_xlabel('年份',fontsize=12)axes1.set_ylabel('电影数量',fontsize=12)axes1.set_xticks(x[::8]) # x轴刻度y_ticks = [i for i in range(0,31,3)]axes1.set_yticks(y_ticks) # y轴刻度axes1.grid(linestyle='--',alpha=0.5) # 网格线

fillna() 作用:用指定的数据填充空值(NaN) 如果"年份"为空,就使用"上映时间"中的年份补充。 统计对比不同语言电影数量。(柱状图)

# 不同语言的电影数量language_count = data.groupby('语言')['语言'].count().sort_values(ascending=False)x_lanuage = language_count.index.tolist()y_lanuage = language_count.values.tolist()#绘制柱装图axes2.bar(x_lanuage,y_lanuage,color='green',width=0.7) #柱状图axes2.set_title("不同语言的电影数量",fontsize=18)axes2.set_xlabel('语言',fontsize=12) # x轴标签axes2.set_ylabel('电影数量',fontsize=12) # y轴标签axes2.grid(linestyle='--',alpha=0.5) # 网格线axes2.tick_params(axis='x', rotation=45) # x轴标签旋转

 统计对比不同类型电影数量。(柱状图)

# 统计对比不同类型电影数量。(柱状图)type_count = {}for types in data['类型'].str.split(','):    for type in types:        if type in type_count:            type_count[type] += 1        else:            type_count[type] = 1x_types = list(type_count.keys())y_types = list(type_count.values())#绘制柱装图axes3.bar(x_types,y_types,color='green',width=0.7) #柱状图axes3.set_title("不同类型的电影数量",fontsize=18)axes3.set_xlabel('类型',fontsize=12) # x轴标签axes3.set_ylabel('电影数量',fontsize=12) # y轴标签axes3.grid(linestyle='--',alpha=0.5) # 网格线axes3.tick_params(axis='x', rotation=45) # x轴标签旋转

 统计对比各个电影评分的比例。(饼状图)

# 统计对比各个电影评分的比例score_count =  data.groupby('评分')['评分'].count()scores = score_count.index.tolist()scores_values = score_count.values.tolist()# 合并小数据(比例小于2%) --> 其他total = score_count.sum()large_scores = score_count.loc[score_count >= total * 0.02] #大数据small_scores = score_count.loc[score_count < total * 0.02] #小数据if small_scores.shape[0] > 0:    other_count = small_scores.sum()    large_scores['其他'] = other_count# 绘制饼状图axes4.pie(large_scores, labels=large_scores.index, autopct='%1.1f%%', startangle=0,radius=1.2) # startangle 起始角度axes4.set_title("不同评分的电影数量占比比例",fontsize=18)axes4.legend(loc='lower center',ncol=4,bbox_to_anchor=(0.5,-0.19))

loc 是 Pandas 中基于标签(label)的索引器,用于通过行标签和列标签来获取数据。 loc 根据布尔条件的 True/False 来筛选数据:

  • True:保留该行数据
  • False:过滤掉该行数据

完整代码

import pandas as pdimport matplotlib.pyplot as pltfrom matplotlib.axes import Axes#展示中文plt.rcParams['font.sans-serif'] = ['SimHei']# 创建子图fig,axes = plt.subplots(nrows=2,ncols=2,figsize=(20,12),dpi = 100)fig.suptitle('TMDB-TOP300电影榜单数据统计',fontsize=23,x=0.5,y=0.95)plt.subplots_adjust(hspace=0.35, wspace=0.3)#获取子图axes1:Axes = axes[0][0]axes2:Axes = axes[0][1]axes3:Axes = axes[1][0]axes4:Axes = axes[1][1]#加载数据data = pd.read_csv('../data/movies.csv',usecols=['电影名','年份','上映时间','类型','时长','评分','语言'],dtype={'年份':'Int64'})# 统计TOP300的电影中,每一年上映的电影数量的变化。(折线图)# 缺失值、异常值处理data['年份'] = data['年份'].fillna(data['上映时间'].str[:4])#分组统计yeart_count =  data.groupby('年份')['年份'].count()# 组装数据# x轴数据min_yaer = yeart_count.index.min()max_year = yeart_count.index.max()x = [ i for i in range(min_yaer,max_year + 1)]# y轴数据y = [int(yeart_count.get(i, 0)) for i in x]# 绘制折线图axes1.plot(x,y,color='green')axes1.set_title("每年电影数量变化折线图",fontsize=18)axes1.set_xlabel('年份',fontsize=12)axes1.set_ylabel('电影数量',fontsize=12)axes1.set_xticks(x[::8]) # x轴刻度y_ticks = [i for i in range(0,31,3)]axes1.set_yticks(y_ticks) # y轴刻度axes1.grid(linestyle='--',alpha=0.5) # 网格线# 不同语言的电影数量language_count = data.groupby('语言')['语言'].count().sort_values(ascending=False)x_lanuage = language_count.index.tolist()y_lanuage = language_count.values.tolist()#绘制柱装图axes2.bar(x_lanuage,y_lanuage,color='green',width=0.7) #柱状图axes2.set_title("不同语言的电影数量",fontsize=18)axes2.set_xlabel('语言',fontsize=12) # x轴标签axes2.set_ylabel('电影数量',fontsize=12) # y轴标签axes2.grid(linestyle='--',alpha=0.5) # 网格线axes2.tick_params(axis='x', rotation=45) # x轴标签旋转# 统计对比不同类型电影数量。(柱状图)type_count = {}for types in data['类型'].str.split(','):    for type in types:        if type in type_count:            type_count[type] += 1        else:            type_count[type] = 1x_types = list(type_count.keys())y_types = list(type_count.values())#绘制柱装图axes3.bar(x_types,y_types,color='green',width=0.7) #柱状图axes3.set_title("不同类型的电影数量",fontsize=18)axes3.set_xlabel('类型',fontsize=12) # x轴标签axes3.set_ylabel('电影数量',fontsize=12) # y轴标签axes3.grid(linestyle='--',alpha=0.5) # 网格线axes3.tick_params(axis='x', rotation=45) # x轴标签旋转# 统计对比各个电影评分的比例score_count =  data.groupby('评分')['评分'].count()scores = score_count.index.tolist()scores_values = score_count.values.tolist()# 合并小数据(比例小于2%) --> 其他total = score_count.sum()large_scores = score_count.loc[score_count >= total * 0.02] #大数据small_scores = score_count.loc[score_count < total * 0.02] #小数据if small_scores.shape[0] > 0:    other_count = small_scores.sum()    large_scores['其他'] = other_count# 绘制饼状图axes4.pie(large_scores, labels=large_scores.index, autopct='%1.1f%%', startangle=0,radius=1.2) # startangle 起始角度axes4.set_title("不同评分的电影数量占比比例",fontsize=18)axes4.legend(loc='lower center',ncol=4,bbox_to_anchor=(0.5,-0.19))# 保存图片plt.savefig("../data/TMDB-TOP300.png")plt.show()

FastAPI 基础

Web初识

Web网站由三个核心部分组成:前端程序、服务端程序、数据库。 前端程序:负责前端页面的展示,网页由 HTML(负责网页的结构)、CSS(负责网页的表现)、JavaScript(负责网页的动作) 三个部分组成 。 服务端程序:可以基于Python中的Django、Flask 或 FastAPI来进行开发。

FastAPI入门

FastAPI是一个现代、快速、高性能的Web框架,用于基于标准的Python类型提示构建API接口服务。 官网:https://fastapi.org.cn

API接口:应用程序编程接口(Application Programming Interface),就是对外提供的功能入口,供别人来调用(比如:天气查询的API接口)。

导入FastAPI

from fastapi import FastAPI

创建FastAPI实例对象

app = FastAPI()

创建路径操作函数,定义访问路径

# 定义功能接口@app.get("/")def root():    return {"message": "Hello World"}@app.get("/user")def get_user():    return {"user": "ZhangSan",            "age": 18            }

运行FastAPI服务

# 启动FastAPI服务器if __name__ == "__main__":    import uvicorn    uvicorn.run(app, host="0.0.0.0", port=8000)
fastapi dev "xxxx.py"uvicorn xxxx:app --reload

汉字迷盒案例

开发规范

Restful指的是遵循REST架构风格的API接口服务,而REST(REpresentational State Transfer),表述性状态转换,它是一种软件架构风格。 Restful指的是遵循REST架构风格的API接口服务,而REST(REpresentational State Transfer),表述性状态转换,它是一种软件架构风格。

注意:REST是风格,是约定方式,约定不是规定,可以打破 。 注意:描述功能模块通常使用复数形式(加s),表示此类资源,而非单个资源。如:users、books、items 。

基础环境准备

 汉字谜盒 是一款基于人工智能的字谜互动游戏,专为汉字爱好者设计。在这里,你将与AI机器人进行有趣的猜字挑战! AI会随机出一道经典字谜(如"一箭穿心"),你需要根据谜面提示猜出对应的汉字,AI会根据你的回答给出相应的提示,并给出最终的答案。

from fastapi import FastAPIfrom starlette.responses import FileResponsefrom fastapi.staticfiles import StaticFilesapp = FastAPI(title="汉字迷盒")# 挂载静态文件的存放目录app.mount("/static",StaticFiles(directory="static"),name="static")#定义路径操作函数@app.get('/')def root():    print("访问项目首页")    return FileResponse('static/index.html')if __name__ == '__main__':    import uvicorn    uvicorn.run(app,host='127.0.0.1',port=8000)

挂载静态文件 将static文件夹映射到URL路径/static 参数说明:

  • “/static”:URL访问路径
  • directory=“static”:本地文件夹名称
  • name=“static”:路由名称标识

效果:访问http://127.0.0.1:8000/static/xxx.css就能获取static/xxx.css文件

新建会话

在打开该项目页面时,如果之前没有会话,则要自动创建一个会话,会话标识形式如 2026-04-15_12-00-05。

# 新建会话@app.post("/api/sessions")def create_session():    print("新建会话")    # 生成会话标识    session_id = generate_session_id()    # 组装会话信息,保存到文件中    session_data = {        'current_session': session_id,        'messages': []    }    with open(os.path.join("sessions", session_id + ".json"), "w", encoding="utf-8") as f:        json.dump(session_data, f, ensure_ascii=False, indent=4)    # 返回数据    return {        'code': 200,        'message': '会话创建成功',        'data': session_id    }
# 数据模型class ApiResponse(BaseModel):    code: int    message: str    data: Any  # 任意类型数据# 新建会话@app.post("/api/sessions")def create_session():    print("新建会话")    # 生成会话标识    session_id = generate_session_id()    # 组装会话信息,保存到文件中    session_data = {        'current_session': session_id,        'messages': []    }    with open(os.path.join("sessions", session_id + ".json"), "w", encoding="utf-8") as f:        json.dump(session_data, f, ensure_ascii=False, indent=4)    # 返回数据    return ApiResponse(code=200, message="会话创建成功", data=session_id)

与AI交互

在会话页面,我们就可以输入我们的问题或者答案,然后请求服务端与AI进行交互了。 在与AI进行交互式,前端传递给服务端的参数包含两项,分别为session_id 与 message,并以json格式在请求体中传递到服务端。

BaseModel:是Pydantic库提供的父类(FastAPI 深度集成了 Pydantic),用于定义FastAPI数据模型和数据验证规则。

class ChatRequest(BaseModel):    session_id: str    message: str# 与Ai交互@app.post("/api/chat")def chat(request: ChatRequest):    print(f"与AI交互:{request.session_id} : {request.message}")    return ApiResponse.success(message='OK',data="AI大模型返回的数据")

在会话页面,我们就可以输入我们的问题或者答案,然后请求服务端与AI进行交互了。

# 与Ai交互client = OpenAI(    api_key="xxxx",    base_url="https://api.deepseek.com")#根据session_id 获取文件名def get_session_file(session_id: str):    return os.path.join("sessions", session_id + ".json")@app.post("/api/chat")def chat(request: ChatRequest):    print(f"与AI交互:{request.session_id} : {request.message}")    # 逻辑实现    # 1.加载json文件中的会话数据    session_file = get_session_file(request.session_id)    with open(session_file, "r", encoding="utf-8") as f:        session_data = json.load(f)    # 2. 构建Ai大模型交互的数据信息    messages = [{'role': 'system', 'content': SYSTEM_PROMPT}]    for message in session_data['messages']:        messages.append(message)    messages.append({'role': 'user', 'content': request.message})    # 3. 调用AI大模型 deepseek    response = client.chat.completions.create(        model="deepseek-chat",        messages=messages,        stream=False,        temperature=1.5,    )    # 4.获取响应数据    ai_response = response.choices[0].message.content    # 5.更新消息列表中的消息    messages.append({'role': 'assistant', 'content': ai_response})    session_data['messages'] = messages    # 6.保存会话信息到json文件中    with open(session_file, "w", encoding="utf-8") as f:        json.dump(session_data, f, ensure_ascii=False, indent=4)    # 7.返回数据    return ApiResponse.success(message='OK',data=ai_response)

会话列表

在汉字谜盒项目的左侧侧边栏,要查询并展示出所有的会话信息,将会话名展示在左侧, 并且根据时间倒序排序。

@app.get('/api/sessions')def get_sessions():    print("获取会话列表")    # 获取sessions文件夹下的所有目录名    session_files = os.listdir("sessions")    # 获取文件名中的会话Id    session_ids = [file.split('.')[0] for file in session_files]    session_ids.sort(reverse=True)    return ApiResponse.success(message="会话列表获取成功", data=session_ids)

加载指定会话

在点击左侧的会话名称之后,就要查询出该会话对应的会话信息,并在消息展示栏将其展示出来。

# 获取指定会话信息@app.get('/api/sessions/{session_id}')def get_session(session_id: str):    print(f"获取会话信息:{session_id}")    session_file = get_session_file(session_id)    with open(session_file, "r", encoding="utf-8") as f:        session_data = json.load(f)    return ApiResponse.success(message="会话信息获取成功", data=session_data)

删除会话

在点击左侧会话名称之后的 x ,就要将当前的会话信息直接删除掉 。

# 删除指定会话信息@app.delete('/api/sessions/{session_id}')def delete_session(session_id: str):    print(f"删除会话信息:{session_id}")    session_file = get_session_file(session_id)    if os.path.exists(session_file):        os.remove(session_file)        return ApiResponse.success(message="会话信息删除成功",data=None)    else:        return ApiResponse.success(message="会话信息不存在",data=None)

日志记录

为了能够灵活的控制项目中日志的输出,我们可以通过官方提供的logging模块来输出日志,具体做法如下:

日志级别:日志级别就是给日志信息贴上的"重要性标签",常见的级别有:DEBUG、INFO、WARNING、ERROR、FATAL(日志级别依次升高)。

import logginglogging.basicConfig(    format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',    level=logging.INFO)

异常处理

项目中的功能较多,目前我们并未考虑异常处理,可以借助于FastAPI中的统一异常处理方案来处理异常。

# 异常处理器 统一处理异常信息# 异常处理器 统一处理异常信息@app.exception_handler(Exception)def handle_exception(request, exc: Exception):    logging.error(f"处理异常,请求路径:{request.url}, 异常信息:{exc}")    return JSONResponse(content={"code": 500, "message": "服务器错误"})

 完整代码

import os.pathfrom datetime import datetimeimport jsonfrom typing import Anyfrom fastapi import FastAPIfrom pydantic import BaseModelfrom starlette.responses import FileResponse, JSONResponsefrom fastapi.staticfiles import StaticFilesfrom openai import OpenAIimport logginglogging.basicConfig(    format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',    level=logging.INFO)app = FastAPI(title="汉字迷盒")# 挂载静态文件的存放目录app.mount("/static",StaticFiles(directory="static"),name="static")# 系统提示词SYSTEM_PROMPT = """# 角色定义你是一个专门玩猜字谜的AI小助手,只进行字谜互动,不闲聊无关内容,全程纯文本交互,不使用表情符号。## 核心能力- 出字谜、判对错、给提示- 记忆已用谜题,确保会话内不重复- 简洁明快回应## 出题规则(严格执行!)1. 开场先友好打招呼,并随机出一道常见、简单、适合大众并必须符合逻辑推理的字谜,禁止使用生僻、低俗、网络烂梗。2. 题目格式:“谜面”(打一字)。3. 每次出题必须完全随机,禁止重复使用相同题目,也可以偶尔穿插使用,下面示例中的谜语。4. 新出题目时, 不要提示, 用户需要提示时, 或者答错时, 再给予合理的提示。## 判题规则(严格执行!)1. 用户只回复一个字时,直接视为答案。2. 答对:立即夸奖并揭晓谜底,格式如“太棒了!就是‘X’字!要不要再来一题?”3. 答错:告知不对,可给一句简短提示,但不泄露答案。格式如“不对哦,再想想~”4. 严禁在用户答错后直接公布答案!只有用户说“公布答案”或“不知道”等情况时才公布。## 互动流程1. 用户答对:夸奖 + 确认正确 + 询问“要不要再来一题?”2. 用户答错:告知不对 + 简单提示 + 鼓励继续猜3. 用户说“提示一下”:给出简短线索,不公布答案4. 用户说“公布答案”或“不知道”:揭晓谜底并解释 + 询问“要不要再来一题?”5. 用户说“换一题”“再来一题”:立即更换新字谜## 回复风格约束- 语气轻松有趣,但保持简洁- 全程只围绕字谜,拒绝回答其他问题- 回复不超过3句话- **绝对不要在回复中说“这个出过了,我来个新的”或类似表述** — 直接给出新谜语即可- 判题错误零容忍,不确定谜底时,先回复“我再想想”而不是乱判## 常见谜语类型及谜底参考示例, 仅仅为参照示例### 组合类- 「一加一不是二」= 王- 「二人不是天」= 夫- 「十口不是田」= 古### 包含类- 「一人在内」= 肉- 「口里有人」= 囚- 「门里有口」= 问- 「田里长草」= 苗- 「心里有你」= 您- 「山里有山」= 出- 「王头上有人」= 全- 「水上有石」= 泵### 半取类- 「半吃半拿」= 哈- 「半真半假」= 值- 「半青半紫」= 素- 「半朋半友」= 有- 「半推半就」= 扰- 「半山半水」= 汕### 象形类- 「三人又重逢」= 众- 「一口咬掉牛尾巴」= 告- 「两座山」= 出- 「三日又重逢」= 晶"""client = OpenAI(    api_key="xxxxx",    base_url="https://api.deepseek.com")#创建会话存放目录if not os.path.exists("sessions"):    os.mkdir("sessions")def generate_session_id():    return datetime.now().strftime("%Y-%m-%d_%H-%M-%S")#根据session_id 获取文件名def get_session_file(session_id: str):    return os.path.join("sessions", session_id + ".json")# 数据模型class ApiResponse(BaseModel):    code: int    message: str    data: Any  # 任意类型数据    @staticmethod    def success( message: str, data: Any):        return {"code": 200, "message": message, "data": data}#定义路径操作函数@app.get('/')def root():    logging.info("访问项目首页")    return FileResponse('static/index.html')# 新建会话@app.post("/api/sessions")def create_session():    logging.info("新建会话")    # 生成会话标识    session_id = generate_session_id()    # 组装会话信息,保存到文件中    session_data = {        'current_session': session_id,        'messages': []    }    with open(get_session_file(session_id), "w", encoding="utf-8") as f:        json.dump(session_data, f, ensure_ascii=False, indent=4)    # 返回数据    return ApiResponse.success(message="会话创建成功", data=session_id)class ChatRequest(BaseModel):    session_id: str    message: str# 与Ai交互@app.post("/api/chat")def chat(request: ChatRequest):    logging.info(f"与AI交互:{request.session_id} : {request.message}")    # 逻辑实现    # 1.加载json文件中的会话数据    session_file = get_session_file(request.session_id)    with open(session_file, "r", encoding="utf-8") as f:        session_data = json.load(f)    # 2. 构建Ai大模型交互的数据信息    messages = [{'role': 'system', 'content': SYSTEM_PROMPT}]    for message in session_data['messages']:        messages.append(message)    messages.append({'role': 'user', 'content': request.message})    # 3. 调用AI大模型 deepseek    response = client.chat.completions.create(        model="deepseek-chat",        messages=messages,        stream=False,        temperature=1.5,    )    # 4.获取响应数据    ai_response = response.choices[0].message.content    # 5.更新消息列表中的消息    messages.append({'role': 'assistant', 'content': ai_response})    session_data['messages'] = messages    # 6.保存会话信息到json文件中    with open(session_file, "w", encoding="utf-8") as f:        json.dump(session_data, f, ensure_ascii=False, indent=4)    # 7.返回数据    return ApiResponse.success(message='与AI交互成功',data=ai_response)@app.get('/api/sessions')def get_sessions():    logging.info("获取会话列表")    # 获取sessions文件夹下的所有目录名    session_files = os.listdir("sessions")    # 获取文件名中的会话Id    session_ids = [file.split('.')[0] for file in session_files]    session_ids.sort(reverse=True)    return ApiResponse.success(message="会话列表获取成功", data=session_ids)# 获取指定会话信息@app.get('/api/sessions/{session_id}')def get_session(session_id: str):    logging.info(f"获取会话信息:{session_id}")    session_file = get_session_file(session_id)    with open(session_file, "r", encoding="utf-8") as f:        session_data = json.load(f)    return ApiResponse.success(message="会话信息获取成功", data=session_data)# 删除指定会话信息@app.delete('/api/sessions/{session_id}')def delete_session(session_id: str):    logging.info(f"删除会话信息:{session_id}")    session_file = get_session_file(session_id)    if os.path.exists(session_file):        os.remove(session_file)        return ApiResponse.success(message="会话信息删除成功",data=None)    else:        return ApiResponse.success(message="会话信息不存在",data=None)# 异常处理器 统一处理异常信息@app.exception_handler(Exception)def handle_exception(request, exc: Exception):    logging.error(f"处理异常,请求路径:{request.url}, 异常信息:{exc}")    return JSONResponse(content={"code": 500, "message": "服务器错误"})if __name__ == '__main__':    import uvicorn    uvicorn.run(app,host='127.0.0.1',port=8000)

总结

Python生态系统的强大之处在于,它能让开发者用一套语言完成从数据采集、分析、可视化到Web应用、AI集成的全链路开发。希望通过本文的学习,你不仅掌握了具体的技术实现,更重要的是建立了"全栈思维"——能够从宏观角度设计系统架构,选择合适的工具链,最终交付高质量的技术解决方案。

技术之路,贵在实践。建议你在阅读完本文后,动手运行代码、调试功能、甚至在此基础上进行二次创新。唯有亲手敲过代码,才能真正将这些知识内化为自己的能力。祝你在Python开发的道路上越走越远!

最新文章

随机文章