Flask or FastAPI? Python服务端初体验

news2025/1/11 0:25:44

1. 引言

最近由于工作需要,又去了解了一下简单的python服务搭建的相关工作,主要是为了自己开发的模型或者工具给同组的人使用。之前介绍的针对于数据科学研究比较友好的一个可以展示的前端框架Streamlit可以说是一个利器。不过,随着ChatGPT的流行,基于chat的服务越来越多了起来,streamlit有一个chat衍生物streamlit-chat,但是它能提供的只是一个简单的聊天功能,并不能具有更高级显示,例如支持markdown和流式输出等。因此,更加适合大模型前端的FastChat可能是更好的选择。

话说回来,前端只是一个展示的界面,而真正提供服务的,需要后端才行。严格意义上讲,后端都是提供数据服务的,也就是对数据进行增删查改的,本质是离不开的,但是现在不是和数据库链接,而是和模型或者模型服务链接了。

这时候,当我去搜索python的后端库的时候,最长用的中小应用web框架Flask出现在我面前。但是我感觉还是有点复杂,于是我想到了一个基于Starlette二次开发的FastAPI,也许更适合。(这里有他们之间的比较介绍,介绍1,介绍2,介绍3,介绍4)。当然,也有人指出,用Flask与FastAPI比较式不公平的,就像是比较苹果和橙汁哪个更甜一样。Flask作为通用web框架,应该和Starlette比。我关于这点也是认同的。但这更能说明,在一个需要快速开发而需求不是那么重的时候,FastAPI是一个更好的选择。(当然我也用Flask开发过多线程支持的服务,两者各有千秋。)

2. Hello world

正如任何语言的第一个案例一样,我们首先用一个非常简单的例子介绍使用FastAPI提供服务的。

第一步,准备代码app.py

from fastapi import FastAPI
from pydantic import BaseModel
from gpt import GPT

app = FastAPI()
model = GPT()

class Message(BaseModel):
    new_message: str
    role: str = ""
    args: dict = {}

@app.post('/gpt')
def gpt_endpoint(message: Message):
    new_message = message.new_message
    role = message.role
    args = message.args

    response = model.call(new_message=new_message, role=role, args=args)
    return response

从这段不足50行的代码里,凸显出了大部分的FastAPI的特性。可以看到主体app,自己的资源model以及一个数据类Message,一个post接口服务(名为/gpt),并做了一些操作,返回response。

这里的GPT是简化的类,其处理代码可以写到另一个文件中。

第二步,安装依赖库

pip install fastapi
pip install uvicorn
pip install pydantic

第三步,运行服务

uvicorn app:app --host 0.0.0.0 --port 8000

经历以上三步,一个FastAPI服务就搭建好了,访问地址例如:http://localhost:8000/gpt,如何快速的测试它呢?FastAPI自带了Docs,可以通过URL访问(http://localhost:8000/docs),如下图所示:
在这里插入图片描述

如果代码中注释写的足够多的话,都不用另写手册了。

但是刚才的代码只是向我们简单的介绍了一下FastAPI的特性,如果我需要构建更加复杂的服务呢?下面我们通过2个实战例子更加全面的介绍FastAPI的使用。

3. 扩充实战

3.1 使用FastAPI提供增删查改的例子

假设我们正在开发一个简单的待办事项管理应用程序。我们希望实现以下功能:

  • 获取所有待办事项的列表
  • 创建一个新的待办事项
  • 更新特定待办事项的内容
  • 标记特定待办事项为已完成
  • 删除特定待办事项

我们可以使用FastAPI来实现这些功能。以下是使用不同类型装饰器定义的API端点的示例代码:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# 待办事项数据模型
class TodoItem(BaseModel):
    id: int
    title: str
    completed: bool = False

# 模拟的待办事项存储
todo_items = []

# 获取所有待办事项的列表
@app.get("/todos")
def get_todo_list():
    return todo_items

# 创建一个新的待办事项
@app.post("/todos")
def create_todo_item(item: TodoItem):
    todo_items.append(item)
    return item

# 更新特定待办事项的内容
@app.put("/todos/{item_id}")
def update_todo_item(item_id: int, item: TodoItem):
    for i in range(len(todo_items)):
        if todo_items[i].id == item_id:
            todo_items[i] = item
            return item

# 标记特定待办事项为已完成
@app.patch("/todos/{item_id}")
def complete_todo_item(item_id: int):
    for item in todo_items:
        if item.id == item_id:
            item.completed = True
            return item

# 删除特定待办事项
@app.delete("/todos/{item_id}")
def delete_todo_item(item_id: int):
    for item in todo_items:
        if item.id == item_id:
            todo_items.remove(item)
            return {"message": "Item deleted"}


在上面的示例中,我们使用了以下不同类型的装饰器:

  1. @app.get(path: str):定义GET请求的端点。使用@app.get(“/todos”)装饰器定义了获取所有待办事项的列表的端点。这个端点不需要接收任何参数,直接返回待办事项列表。

  2. @app.post(path: str):定义POST请求的端点。使用@app.post(“/todos”)装饰器定义了创建新待办事项的端点。这个端点接收一个请求体参数item,它是一个TodoItem模型的实例,包含了待办事项的内容。在处理函数中,我们将新的待办事项添加到todo_items列表中,并返回添加的待办事项。

  3. @app.put(path: str):定义PUT请求的端点。使用@app.put(“/todos/{item_id}”)装饰器定义了更新待办事项的端点。这个端点接收一个路径参数item_id用于指定待办事项的ID,以及一个请求体参数item,它是一个TodoItem模型的实例,包含了待办事项的新内容。在处理函数中,我们遍历todo_items列表找到对应ID的待办事项,将其更新为新的内容,并返回更新后的待办事项。

  4. @app.patch(path: str):定义PATCH请求的端点。使用@app.patch(“/todos/{item_id}”)装饰器定义了标记待办事项为已完成的端点。这个端点接收一个路径参数item_id用于指定待办事项的ID。在处理函数中,我们遍历todo_items列表找到对应ID的待办事项,并将其标记为已完成(item.completed = True),然后返回已更新的待办事项。

  5. @app.delete(path: str):定义DELETE请求的端点。使用@app.delete(“/todos/{item_id}”)装饰器定义了删除待办事项的端点。这个端点接收一个路径参数item_id用于指定待办事项的ID。在处理函数中,我们遍历todo_items列表找到对应ID的待办事项,并将其从列表中删除,然后返回一个简单的消息表示删除成功。

  6. 这些装饰器允许我们根据HTTP方法(GET、POST、PUT、PATCH、DELETE)来定义不同类型的端点,并通过路径参数和请求体参数来接收和处理不同的数据。这样,我们可以使用统一的应用程序来处理各种操作,并根据RESTful API的原则设计我们的API。

这里,我们注意到PUT和PATCH方法是很相似的,都是用于更新的,但是两者有以下不同:

PUT请求用于完全替换(Replace)服务器上的资源或实体。当客户端发送一个PUT请求时,它需要提供完整的资源表示,包括要更新的所有字段。
如果资源不存在,则会创建一个新的资源;如果资源存在,则会完全替换(覆盖)现有资源的内容。
客户端通常应该提供完整的资源表示,即使只有部分字段发生更改。这意味着客户端需要提供所有字段,而不仅仅是要更改的字段。
PUT请求是幂等的,多次执行相同的PUT请求不会对资源产生额外的影响。

PATCH请求用于对服务器上的资源或实体进行部分更新。当客户端发送一个PATCH请求时,它只需要提供要更新的部分字段或属性。
PATCH请求可以用于增量更新资源的特定字段,而不需要发送整个资源的表示。这使得它适用于只更新部分字段的情况。
服务器根据客户端提供的更新内容,选择性地更新资源的相应字段。未提供的字段将保持不变。
PATCH请求可以是幂等的,但也可以是非幂等的,这取决于具体实现和使用情况。

在实际应用中,可以根据具体的业务需求和设计准则来选择使用PUT还是PATCH请求。如果要更新整个资源或实体,应该使用PUT请求。如果只需更新部分字段或属性,应该使用PATCH请求。

3.2 使用FastAPI提供GPU加载语言模型的例子

import uvicorn
import torch
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel

# 示例GPT模型类
class GPTModel:
    def __init__(self):
        # 这里是你的GPT模型的初始化代码
        # ...

    def generate_text(self, input_text):
        # 这里是生成文本的代码
        # ...

# 示例后台任务函数
def load_model(background_tasks):
    # 加载和初始化GPT模型
    model = GPTModel()
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)
    model.eval()

    # 将模型保存到FastAPI应用的state中
    app.state.model = model

# 示例请求模型的输入
class TextRequest(BaseModel):
    text: str

app = FastAPI()

@app.post("/generate")
def generate_text(request: TextRequest, background_tasks: BackgroundTasks):
    # 在后台任务中加载模型
    background_tasks.add_task(load_model, background_tasks)

    # 从应用程序状态中获取模型
    model = app.state.model

    # 在GPU上进行推理
    input_text = request.text
    generated_text = model.generate_text(input_text)

    # 返回生成的文本结果
    return {"generated_text": generated_text}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

4. 小结

本文主要简单的介绍了FastAPI的一些简单用法,方便快速搭建原型服务。

(随着ChatGPT等大模型逐渐渗透到我们的生活中,可能如此记录技术细节的博客愈发的没有用了。因为不会的东西ChatGPT可以交互式的给予指导,而不需要再这样看博客了。尽管这博客也离不开ChatGPT的指示,我还是看了一些相关博客,也自己亲手试了ChatGPT给的代码,多少增加一些可信度。)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/580681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

由前序和中序创建二叉树

算法分析 首先,前序是按照 根 -> 左子树 -> 右子树 这样的顺序来进行访问的,也就是说,前序给出的顺序一定是先给出根结点的,那么我们就可以根据前序的顺序来依次递归判断出每个子树的根结点了。 如下所示: 我…

源码角度分析多线程并发情况下数据异常回滚方案

一、 多线程并发情况下数据异常回滚解决方案 在需要多个没有前后顺序的数据操作情况下,一般我们可以选择使用并发的形式去操作,以提高处理的速度,但并发情况下,我们使用 Transactional 还能解决事务回滚问题吗。 例如有下面表结…

Go语言并发

Go语言并发学习目标 出色的并发性是Go语言的特色之一 • 理解并发与并行• 理解进程和线程• 掌握Go语言中的Goroutine和channel• 掌握select分支语句• 掌握sync包的应用 并发与并行 并发与并行的概念这里不再赘述, 可以看看之前java版写的并发实践; 进程和线程 程序、进程…

C语言3:根据身份证号输出生年月日和性别

18位身份证号码第7到10位为出生年份(四位数),第11到12位为出生月份,第13 到14位代表出生日期,第17位代表性别,奇数为男,偶数为女。 用户输入一个合法的身份证号,请输出用户的出生年月日和性别。(不要求较验…

Java数据结构之第十三章、字符串常量池

目录 一、创建对象的思考 二、字符串常量池(StringTable) 三、再谈String对象创建 一、创建对象的思考 下面两种创建String对象的方式相同吗? public static void main(String[] args) {String s1 "hello";String s2 "hello";String s3 …

C# | 线性回归算法的实现,只需采集少量数据点,即可拟合整个数据集

C#线性回归算法的实现 文章目录 C#线性回归算法的实现前言示例代码实现思路测试结果结束语 前言 什么是线性回归呢? 简单来说,线性回归是一种用于建立两个变量之间线性关系的统计方法。在我们的软件开发中,线性回归可以应用于数据分析、预测和…

每日一博 - 对称加密算法 vs 非对称加密算法

文章目录 概述一、对称加密算法常见的对称加密算法优点:缺点:Code 二、非对称加密算法常见的非对称加密算法优点:缺点:Code 概述 在信息安全领域中,加密算法是保护数据安全的重要手段。 加密算法可以分为多种类型&am…

【Linux】线程互斥 与同步

文章目录 1. 背景概念多个线程对全局变量做-- 操作 2. 证明全局变量做修改时,在多线程并发访问会出问题3. 锁的使用pthread_mutex_initpthread_metux_destroypthread_mutex_lock 与 pthread_mutex_unlock具体操作实现设置为全局锁 设置为局部锁 4. 互斥锁细节问题5.…

哈夫曼树(Huffman)【数据结构】

目录 ​编辑 一、基本概念 二、哈夫曼树的构造算法 三、哈夫曼编码 假如<60分的同学占5%&#xff0c;60到70分的占15%…… 这里的百分数就是权。 此时&#xff0c;效率最高&#xff08;判断次数最少&#xff09;的树就是哈夫曼树。 一、基本概念 权&#xff08;we…

Zabbix4.0 自动发现TCP端口并监控

java端口很多&#xff0c;每台机器上端口不固定&#xff0c;考虑给机器配置组不同的组挂载模版&#xff0c;相对繁琐。直接使用同一个脚本自动获取机器上java相关的端口&#xff0c;推送到zabbix-server。有服务端口挂了自动推送告警 一、zabbix-agent配置过程 1、用户自定义参…

Apache Doris :Rollup 物化视图

整理了一下目前开启虚拟机需要用到的程序, 包括MySQL,Hadoop,Linux, hive,Doris 3.5 Rollup ROLLUP 在多维分析中是“上卷”的意思&#xff0c;即将数据按某种指定的粒度进行进一步聚合。 1.求每个城市的每个用户的每天的总销售额 select user_id,city,date&#xff0c; sum(…

树的简单介绍

目录 树的概念 ​ 树的相关概念 树的表示 二叉树的概念 特殊的二叉树 二叉树的存储结构 总结 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#…

Diffie-Hellman密钥交换协议(Diffie-Hellman Key Exchange,简称DHKE)

文章目录 Diffie-Hellman密钥交换协议&#xff08;Diffie-Hellman Key Exchange&#xff0c;简称DHKE&#xff09;一、密码学相关的数学基础1. 素数&#xff08;质数&#xff09;2. 模运算3. 费马小定理4. 对数5. 离散对数6. 椭圆曲线常见椭圆曲线1. NIST系列曲线secp256k1 2. …

Django实现人脸识别登录

Django实现人脸识别登录 Demo示例下载 1、账号密码登录 2、人脸识别登录 3、注册 4、更改密码 5、示例网站 点我跳转 一、流程说明 1、注册页面:前端打开摄像头,拍照,点击确定后上传图像 2、后端获取到图像,先通过face_recognition第三方库识别是否能够获取到人脸特征…

开闭原则正确姿势, 使用AOP优雅的记录日志, 非常的哇塞

&#x1f473;我亲爱的各位大佬们好&#x1f618;&#x1f618;&#x1f618; ♨️本篇文章记录的为 JDK8 新特性 Stream API 进阶 相关内容&#xff0c;适合在学Java的小白,帮助新手快速上手,也适合复习中&#xff0c;面试中的大佬&#x1f649;&#x1f649;&#x1f649;。 …

使用腾讯云服务器快速搭建网站教程

已经有了腾讯云服务器如何搭建网站&#xff1f;腾讯云服务器网以腾讯云服务器&#xff0c;借助宝塔面板搭建Web环境&#xff0c;然后使用WordPress博客程序搭建网站&#xff0c;大致分为三步&#xff0c;首先购买腾讯云服务器&#xff0c;然后在腾讯云服务器上部署宝塔面板&…

Vue + Vite 构建 自己的ChartGPT 项目

前期回顾 两分钟学会 制作自己的浏览器 —— 并将 ChatGPT 接入_彩色之外的博客-CSDN博客自定义浏览器&#xff0c;并集合ChatGPT&#xff0c;源码已公开https://blog.csdn.net/m0_57904695/article/details/130467253?spm1001.2014.3001.5501 目录 效果图 代码步骤&am…

【Linux】软件包管理器/编辑器/yum是应用商店?/vim编辑器什么?

本文思维导图&#xff1a; 文章目录 Linux软件安装关于Linux的软件生态 1.Linux软件包管理器&#xff1a;yum到底是什么关于yum指令&#xff1a;关于yum源 2. rzsz指令1. Linux编辑器——vim编辑器vim编辑器的三种主要模式vim编辑器命令模式常用快捷键&#xff1a;vim操作总结…

spring(事务管理)

事物可以看做是由对数据库若干操作组成的一个单元 事务的作用就是为了保证用户的每一个操作都是可靠的&#xff0c;事务中的每一步操作都 必须成功执行&#xff0c;只要有发生异常就回退到事务开始未进行操作的状态,这些操作 要么都完成&#xff0c;要么都取消&#xff0c;从而…

Linux:/dev/tty、/dev/tty0 和 /dev/console 之间的区别

在Linux操作系统中&#xff0c;/dev/tty、/dev/tty0和/dev/console是三个特殊的设备文件&#xff0c;它们在终端控制和输入/输出过程中扮演着重要的角色。尽管它们看起来很相似&#xff0c;但实际上它们之间存在一些重要的区别。本文将详细介绍这三个设备文件之间的区别以及它们…