AI大模型从0到1记录学习 day15

news2025/4/18 16:28:04

14.3.5 互斥锁
1)线程安全问题
线程之间共享数据会存在线程安全的问题。
比如下面这段代码,3个线程,每个线程都将g_num +1 十次:
import time
import threading

def func():
global g_num
for _ in range(10):
tmp = g_num + 1
# time.sleep(0.01)
g_num = tmp
print(f"{threading.current_thread().name}: {g_num}\n", end=“”)

if name == “main”:
g_num = 0
threads = [threading.Thread(target=func, name=f"线程{i}") for i in range(3)]
[t.start() for t in threads]
[t.join() for t in threads]
print(g_num) # 30
结果为30,看似没有问题,这是因为这个修改操作花费的时间太短了,短到我们无法想象。所以,线程间轮询执行时,都能获取到最新的 g_num 值。因此暴露问题的概率就变得微乎其微。
我们添加0.01秒的延迟时间:
import time
import threading

def func():
global g_num
for _ in range(10):
tmp = g_num + 1
time.sleep(0.01)
g_num = tmp
print(f"{threading.current_thread().name}: {g_num}\n", end=“”)

if name == “main”:
g_num = 0
threads = [threading.Thread(target=func, name=f"线程{i}") for i in range(3)]
[t.start() for t in threads]
[t.join() for t in threads]
print(g_num) # 10
可以看到最终结果并不是30。这是因为在修改 g_num 前,有0.01秒的休眠时间,某个线程延时后,CPU立即分配计算资源给其他线程。此时0.01秒的休眠还未结束,这个线程还未将修改后的数据赋值给 g_num,因此其他线程获取到的并不是最新值,所以才出现上面的结果。
2)互斥锁的概念
某个线程要更改共享数据时,先将其锁定,此时其他线程不能更改。直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
3)互斥锁的使用
可以通过 threading.Lock() 创建互斥锁。
使用 lock.acquire([blocking=True][, timeout=-1]) 来获取锁(blocking 如果为 True,线程会阻塞直到获取到锁。如果为 False,线程立即返回。获取锁成功返回 True,否则返回 False。timeout 为等待的超时时间,单位为秒。如果超时仍未获取到锁,则返回 False。)。
使用 lock.release() 释放锁。
案例:
import time
import threading

def func():
global g_num
for _ in range(10):
lock.acquire() # 获取锁
tmp = g_num + 1
time.sleep(0.01)
g_num = tmp
lock.release() # 释放锁
print(f"{threading.current_thread().name}: {g_num}\n", end=“”)

if name == “main”:
g_num = 0
lock = threading.Lock() # 创建锁
threads = [threading.Thread(target=func, name=f"线程{i}") for i in range(3)]
[t.start() for t in threads]
[t.join() for t in threads]
print(g_num) # 30
14.3.6 GIL
Python 全局解释器锁(Global Interpreter Lock, 简称 GIL)是一个锁,同一时间只允许一个线程保持 Python 解释器的控制权,这意味着在任何时间点都只能有一个线程处于执行状态。执行单线程程序时看不到 GIL 的影响,但它可能是 CPU 密集型和多线程代码中的性能瓶颈。GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。
Python于1991年诞生,从操作系统没有线程概念的时代就已经存在了。由于物理上的限制,各CPU厂商在核心频率上的比赛已经被多核所取代。为了利用多核,Python开始支持多线程。而为了解决多线程之间数据完整性和状态同步,于是有了GIL,GIL 提供了线程安全的内存管理。
GIL 的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。我们可能会想 GIL只要释放的勤快效率也不会差,至少也不会比单线程的效率差。理论上是这样。
但实际上,Python为了让各个线程能够平均利用CPU时间,会计算当前已执行的微代码数量,达到一定阈值后就强制释放GIL。而这时也会触发一次操作系统的线程调度(当然是否真正进行上下文切换由操作系统自主决定)。从释放 GIL 到获取 GIL 之间几乎是没有间隙的。所以当其他在其他核心上的线程被唤醒时,大部分情况下主线程已经又再一次获取到 GIL 了。这个时候被唤醒执行的线程只能白白的浪费CPU时间,看着另一个线程拿着 GIL 执行。然后达到切换时间后进入待调度状态,再被唤醒,再等待,以此往复恶性循环。

上述实现方式是较为原始的,Python的每个版本中也在逐渐改进GIL和线程调度之间的互动关系。例如先尝试持有GIL在做线程上下文切换,在IO等待时释放GIL等尝试。但是无法改变的是GIL的存在使得操作系统线程调度的这个本来就昂贵的操作变得更奢侈了。
总之,当你的程序需要进行大量的CPU计算时,GIL会成为性能的瓶颈。即使你有多个线程,GIL也会阻止它们在多个CPU核心上并行执行。实际上,多个线程会轮流获取GIL,这样就不能真正并行地使用多个处理器核心。而对于涉及I/O操作(如文件读写、网络请求等)的程序,GIL的影响较小。因为在I/O操作时,线程会释放GIL,其他线程可以在此时执行,这使得多线程在I/O密集型任务中能更有效地并发。
第 15 章 网络编程
15.1 网络
使用网络能够把多方电脑等设备链接在一起进行数据传递。网络编程就是让在不同的电脑上的软件能够进行数据传递,即进程之间的通信。
15.1.1 网络编程三要素
 IP:网络中每台计算机的唯一标识,通过IP地址可以找到计算机。
 端口:标识进程的逻辑地址,通过端口找到计算机中指定的进程(应用软件)。
 协议:定义通信规则。
15.1.2 TCP/IP协议族
1)通信协议
通信协议是一组用于规定不同设备或计算机之间如何进行数据交换和通信的规则和约定。它定义了通信的各个方面,包括数据的格式、传输的顺序、错误检查机制、如何处理不同情况(如重传丢失的数据包)等。协议的目的是确保在网络中传输的数据能够被正确、可靠地理解和处理。
通信协议可以应用于计算机网络、电话网络、无线通信等领域。在不同的应用场景下,会使用不同的协议来实现数据交换、控制信息传递等任务。
2)TCP/IP
TCP/IP 协议族,简称TCP/IP,是一组通信协议,用于互联网的数据传输和网络通信,定义了数据如何在不同的计算机之间传输和路由。是现代计算机网络中最常用的网络协议之一。TCP/IP得名于该协议家族的两个核心协议:TCP(传输控制协议)和IP(网际协议)。
3)分层网络模型
OSI 七层网络模型由国际标准化组织制定,但其实现过于复杂,且制定周期过长,在其整套标准推出之前,TCP/IP 模型已经在全球范围内被广泛使用。TCP/IP 模型定义了应用层、传输层、网络层、网络接口层这四层网络结构,但并没有给出网络接口层的具体内容,因此在学习和开发中,通常将网络接口层替换为 OSI 七层模型中的数据链路层和物理层来进行理解,这就是五层网络模型。

4)常见网络协议

15.2 IP
15.2.1 什么是IP
IP地址由一串数字组成,用来标识一台电脑在网络中的位置。当设备连接网络,设备将被分配一个IP地址,用作标识。通过IP地址设备间可以互相通讯。IP地址有两个主要功能:标识设备或网络,以及寻址。
Windows下可以在命令提示符中使用ipconfig查看网络适配器的IP。
Linux下可以在终端中使用ifconfig或ip addr查看IP。
15.2.2 子网掩码
IP网络可以在IPv4和IPv6中划分子网。为此将IP地址识别成由两部分组成:网络前缀和主机编号。子网掩码(subnet mask)或无类别域间路由(CIDR)表示法确定了IP地址如何分为网络部分和主机部分。
子网掩码一词仅用于IPv4地址中。但是 IPv4和IPv6都使用CIDR概念和符号。在此,在IP地址后面加斜杠和用于标识网络部分的位数(十进制)。例如:IPv4地址及其子网掩码分别可以是 192.168.10.2 和 255.255.255.0 。因为IP地址的前24位表示网络和子网,所以相同的IP地址和子网的CIDR表示法为192.168.10.2/24。
主机编号全为0,表示网络号,主机编号全为1,表示网络广播。

15.2.3 IPv4地址的分类

15.2.4 公网与私网
公网IP在任何地方都可以访问。而私网IP只能在局域网内访问。
国际规定有一部分IP地址是用于局域网使用,也就是属于私网IP,不在公网中使用的,它们的范围是:
 10.0.0.0~10.255.255.255
 172.16.0.0~172.31.255.255
 192.168.0.0~192.168.255.255
其中127.0.0.1~127.255.255.255用于回路测试,如127.0.0.1可以代表本机IP地址。
网络地址转换(NAT)是一种在IP数据包通过路由器或防火墙时重写来源或目的IP地址或端口的技术。这种技术普遍应用于有多台主机,但只通过一个公有IP地址访问互联网的私有网络中。1990年代中期,NAT是作为一种解决IPv4地址短缺以避免保留IP地址困难的方案而流行起来的,并成了家庭和小型办公室网络连接上的路由器的一个标准特征,因为对他们来说,申请独立的IP地址的代价要高于所带来的效益。
15.2.5 IPv4与IPv6
常见的IP地址分为IPv4与IPv6两大类。IPv4为32位长,通常书写时以四组十进制数字组成,并以点分隔,如:172.16.254.1。IPv6为128位长,通常书写时以八组十六进制数字组成,以冒号分割,如:2001:db8:0🔢0:567:8:1。
随着互联网的快速成长,IPv4的42亿个地址最终于2011年2月3日用尽。相应的科研组织已研究出128位的IPv6,其IP地址数量最高可达3.402823669×1038个,届时每个人家居中的每件电器,每件对象,甚至地球上每一粒沙子都可以拥有自己的IP地址。
15.3 端口
15.3.1 什么是端口
这里的端口指的是逻辑端口,即TCP/IP协议中的端口。端口用于进程(应用软件)在同一设备或不同设备之间通信。每个端口有一个对应的端口号。端口号有65536个。
可以使用netstat -ano查看端口信息。
15.3.2 端口号的分配
1)公认端口
01023,它们紧密绑定于一些服务。通常这些端口的通讯明确表明了某种服务的协议。端口号0是被保留的,不可使用。11023系统保留,只能由root用户使用。
2)动态端口
1024~65536,之所以称为动态端口,是因为它一般不固定分配某种服务,而是动态分配。当一个系统进程或应用程序进程需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配一个供它使用。当这个进程关闭时,同时也就释放了所占用的端口号。
3)常见端口
端口 服务
0/TCP,UDP 保留端口,不使用
7/TCP,UDP Echo(回显)协议
21/TCP,UDP FTP文件传输协议
22/TCP,UDP SSH安全远程登录协议
23/TCP,UDP Telnet终端仿真协议
25/TCP,UDP SMTP简单邮件传输协议
53/TCP,UDP DNS域名服务系统
80/TCP,UDP HTTP超文本传输协议
110/TCP POP3邮局协议第3版
137/TCP,UDP NetBIOS名称服务
138/TCP,UDP NetBIOS数据报文服务
139/TCP,UDP NetBIOS会话服务
143/TCP,UDP IMAP用于检索电子邮件
445/TCP Microsoft-DS (Active Directory、Windows 共享、震荡波蠕虫、Agobot、Zobotworm)
445/UDP Microsoft-DS服务器消息块(SMB)文件共享
666/UDP 毁灭战士,电脑平台上的一系列第一人称射击游戏。
873/TCP Rsync文件同步协议
902 VMware服务器控制台
3306/TCP,UDP MySQL数据库系统
3389/TCP 远程桌面协议(RDP)
15.4 socket套接字
15.4.1 什么是socket
socket(套接字)是同一或不同电脑的进程(任务、应用软件)间通信的一个工具,进程之间想要进行网络通信需要基于socket。只要与网络相关的应用程序或者软件都使用到了socket。
15.4.2 socket的使用
Python中提供了socket模块用于创建套接字。
import socket

AF_INET 用于 Internet 进程间通信;SOCK_STREAM 流式套接字,TCP

tcp_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

AF_INET 用于 Internet 进程间通信;SOCK_DGRAM 数据报套接字,UDP

udp_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
15.5 UDP
15.5.1 什么是UDP
用户数据报协议(UDP:User Datagram Protocol)是一个简单的面向数据报的通信协议。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。
UDP避免了协议栈中执行错误检查和纠正处理的开销,适用于对时间有较高要求的应用程序,因为某些场景下丢弃数据包比等待或重传导致延迟更可取。流媒体、在线游戏流量通常使用UDP传输。
15.5.2 UDP编程
1)UDP编程流程

2)案例
UDP服务端:
“”“udp服务端”“”

import socket

创建udp套接字

udp_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

绑定ip和端口

udp_socket.bind((“127.0.0.1”, 8080))
while True:
# 接收数据
recv_data, client_addr = udp_socket.recvfrom(1024)
client_ip = client_addr[0]
client_port = client_addr[1]
print(f"{client_ip}:{client_port}>> {recv_data.decode(“utf-8”)}")
# 发送数据
udp_socket.sendto(“你好”.encode(“utf-8”), client_addr)

关闭套接字

udp_socket.close()
UDP客户端:
“”“udp客户端”“”

import socket

创建udp套接字

udp_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
while True:
try:
# 发送数据
server_ip = “127.0.0.1”
server_port = 8080
udp_socket.sendto(input(f"{server_ip}:{server_port}<< “).encode(“utf-8”), (server_ip, server_port))
# 接收数据
recv_data, client_addr = udp_socket.recvfrom(1024)
client_ip = client_addr[0]
client_port = client_addr[1]
print(f”{client_ip}:{client_port}>> {recv_data.decode(“utf-8”)}")
except KeyboardInterrupt:
break

关闭套接字

udp_socket.close()
15.6 TCP
15.6.1 什么是TCP
传输控制协议(TCP:Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP协议的运行可划分为三个阶段:连接建立、数据传送和连接终止。
很多重要的机制保证了TCP的可靠性和强壮性,包括:
 使用序号,对收到的TCP报文段进行排序以及检测重复的数据。
 使用校验和检测报文段的错误,即无错传输。
 使用确认和计时器来检测和纠正丢包或延时。
 流控制。
 拥塞控制。
 丢失包的重传。
15.6.2 TCP编程
1)TCP编程流程

2)案例
TCP服务端:
“”“tcp服务端”“”

import socket

创建tcp套接字

tcp_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

绑定ip和端口

tcp_socket.bind((“127.0.0.1”, 8080))

设置监听

tcp_socket.listen(2)

等待客户端连接

client_socket, client_addr = tcp_socket.accept()
while True:
# 接收数据
recv_data = client_socket.recv(1024)
print(f"{client_addr[0]}:{client_addr[1]}>> {recv_data.decode(‘utf-8’)}")
# 发送数据
client_socket.send(“你好”.encode(“utf-8”))

关闭套接字

tcp_socket.close()
TCP客户端:
“”“tcp客户端”“”

import socket

创建tcp套接字

tcp_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

连接服务器

server_ip = “127.0.0.1”
server_port = 8080
tcp_socket.connect((server_ip, server_port))
while True:
try:
# 发送数据
tcp_socket.send(input(f"{server_ip}:{server_port}<< “).encode(“utf-8”))
# 接收数据
recv_data = tcp_socket.recv(1024)
print(f”{server_ip}:{server_port}>> {recv_data.decode(“utf-8”)}")
except KeyboardInterrupt:
break

关闭套接字

tcp_socket.close()
15.7 HTTP
15.7.1 什么是HTTP
HTTP(超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。是万维网的数据通信的基础。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。
HTTP 上的一个典型工作流程是客户端计算机向服务器发出请求,然后服务器发送响应消息。通常,由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如“HTTP/1.1 200 OK”,以及返回的内容,如请求的文件、错误消息、或者其它信息。
15.7.2 HTTP消息结构
1)客户端请求消息
客户端发送一个 HTTP 请求到服务器的请求消息包括以下格式:请求行、请求头、空行和请求体四个部分组成。

(1)请求行
请求方法:如 GET、POST、PUT、DELETE等,指定要执行的操作。
请求 URI:请求的资源路径,通常包括主机名、端口号(如果非默认)、路径和查询字符串。
协议版本:如 HTTP/1.1 或 HTTP/2。
请求行的格式示例:GET /index.html HTTP/1.1
(2)请求头
包含了客户端环境信息、请求体的大小(如果有)、客户端支持的压缩类型等。
常见的请求头包括Host、User-Agent、Accept、Accept-Encoding、Content-Length等。
(3)空行
请求头和请求体之间的分隔符,表示请求头的结束。
(4)请求体
在某些类型的HTTP请求(如 POST 和 PUT)中,请求体包含要发送给服务器的数据。

2)服务端响应消息
HTTP 响应由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

(1)状态行
HTTP 版本:与请求消息中的版本相匹配。
状态码:三位数,表示请求的处理结果,如 200 表示成功,404 表示未找到资源。
状态信息:状态码的简短描述。
状态行的格式示例:HTTP/1.1 200 OK
(2)响应头
包含了服务器环境信息、响应体的大小、服务器支持的压缩类型等。
常见的响应头包括Content-Type、Content-Length、Server、Set-Cookie等。
(3)空行
响应头和响应体之间的分隔符,表示响应头的结束。
(4)响应体
包含服务器返回的数据,如请求的网页内容、图片、JSON数据等。

15.7.3 HTTP请求方法
HTTP/1.1 协议中共定义了八种方法来以不同方式操作指定的资源,HTTP 服务器至少应该实现 GET 和 HEAD 方法,其他方法都是可选的。
1)GET
向指定的资源发出“显示”请求。使用 GET 方法应该只用在读取资料,而不应当被用于产生“副作用”的操作中,例如在网络应用程序中。其中一个原因是 GET 可能会被网络爬虫等随意访问。
2)HEAD
与 GET 方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的元信息(或称元数据)”。
3)POST
向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会建立新的资源或修改现有资源,或二者皆有。每次提交,表单的数据被浏览器用编码到 HTTP 请求的 body 里。
4)PUT
向指定资源位置上传其最新内容。
5)DELETE
请求服务器删除 Request-URI 所标识的资源。
6)TRACE
回显服务器收到的请求,主要用于测试或诊断。
7)OPTIONS
这个方法可使服务器传回该资源所支持的所有 HTTP 请求方法。用“*”来代替资源名称,向 Web 服务器发送 OPTIONS 请求,可以测试服务器功能是否正常运作。
8)CONNECT
HTTP/1.1协议中预留给能够将连接改为隧道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
15.7.4 HTTP状态码
HTTP状态码是服务器对客户端请求的响应,状态码分为五类:
1)1xx(信息状态码)
表示接收的请求正在处理。例如:
 100:继续。客户端应继续其请求。
 101:切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议。
2)2xx(成功状态码)
表示请求正常处理完毕。例如:
 200:请求成功。一般用于 GET 与 POST 请求。
 202:已接受。已经接受请求,但未处理完成。
3)3xx(重定向状态码)
需要后续操作才能完成这一请求。例如:
 300:多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择。
 301:永久移动。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI代替。
 302:临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有 URI。
 304:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源。
 305:使用代理。所请求的资源必须通过代理访问。
4)4xx(客户端错误状态码)
表示请求包含语法错误或无法完成。例如:
 400:客户端请求的语法错误,服务器无法理解。
 403:服务器理解请求客户端的请求,但是拒绝执行此请求。
 404:服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置“您所请求的资源无法找到”的个性页面。
 405:客户端请求中的方法被禁止。
5)5xx(服务器错误状态码)
服务器在处理请求的过程中发生了错误。例如:
 500:服务器内部错误,无法完成请求。
 501:服务器不支持请求的功能,无法完成请求。
 502:作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应。
15.8 案例:发送HTTP请求以及获取响应数据
import requests

一言网的 API 地址

url = ‘https://v1.hitokoto.cn/’

请求参数,指定返回中文内容,这里使用默认的所有类型

params = {
‘c’: ‘a’, # 可以根据需要修改类型,a 代表动画,b 代表漫画等
‘encode’: ‘json’
}

try:
print(f"正在发送 GET 请求到: {url},参数: {params}“)
response = requests.get(url, params=params)
status_code = response.status_code
if status_code == 200:
print(f"请求成功!状态码: {status_code}”)
data = response.json()
hitokoto = data[‘hitokoto’]
from_who = data[‘from_who’] if data[‘from_who’] else ‘未知’
print(f"随机名言: {hitokoto} - {from_who}“)
elif status_code == 404:
print(f"请求的资源未找到!状态码: {status_code}”)
elif status_code == 500:
print(f"服务器内部错误!状态码: {status_code}“)
else:
print(f"发生未知错误,状态码: {status_code}”)
except requests.RequestException as e:
print(f"请求过程中出现错误: {e}")
15.9 案例:通过Starlette 构建web接口
Starlette 是一个轻量级的 Python 异步 Web 框架,专为构建高性能的异步应用程序而设计,它具有简洁、灵活的特点,并且可以与其他库(如 FastAPI 就是基于 Starlette 构建的)很好地集成。我们可以结合 Starlette 构建一个Web 服务,将上面获取随机名言的功能封装成一个 API 接口,这样可以带来一些优势,例如实现更灵活的交互、支持多用户访问。
 Uvicorn:它是一个基于 Python 的 ASGI(Asynchronous Server Gateway Interface)服务器。ASGI 是 Python 中用于异步 Web 应用的标准接口,Uvicorn 能够高效地处理并发请求,基于 uvloop(一个快速的异步事件循环)和 httptools(一个快速的 HTTP 解析器)构建,为 Python 异步 Web 应用提供了高性能的运行环境。
 Starlette:是一个轻量级的 Python 异步 Web 框架,它遵循 ASGI 标准,专注于提供简洁、灵活的 API 来构建 Web 应用和服务。Starlette 提供了路由、中间件、请求和响应处理等核心功能,允许开发者快速搭建 Web 应用的逻辑。
 协作方式:Uvicorn 为 Starlette 应用提供了运行的基础环境。当你使用 Starlette 编写好一个 Web 应用后,无法直接运行,需要借助像 Uvicorn 这样的 ASGI 服务器来启动和部署
1)安装依赖包
pip install starlette uvicorn requests
2)代码实现
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
import requests
import uvicorn

一言网的 API 地址

HITOKOTO_URL = ‘https://v1.hitokoto.cn/’

定义异步函数来获取随机名言

async def get_hitokoto():
try:
# 请求参数,指定返回中文内容,这里使用默认的所有类型
params = {
‘c’: ‘a’, # 可以根据需要修改类型,a 代表动画,b 代表漫画等
‘encode’: ‘json’
}
response = requests.get(HITOKOTO_URL, params=params)
status_code = response.status_code
if status_code == 200:
data = response.json()
hitokoto = data[‘hitokoto’]
from_who = data[‘from_who’] if data[‘from_who’] else ‘未知’
return {‘hitokoto’: hitokoto, ‘from_who’: from_who}
else:
return {‘error’: f’请求一言网 API 失败,状态码: {status_code}‘}
except requests.RequestException as e:
return {‘error’: f’请求过程中出现错误: {str(e)}’}

定义处理根路径请求的异步函数

async def homepage(request):
result = await get_hitokoto()
return JSONResponse(result)

创建 Starlette 应用实例

app = Starlette(debug=True, routes=[
Route(‘/’, homepage),
])

if name == “main”:
# 使用 uvicorn 运行应用
uvicorn.run(app, host=‘0.0.0.0’, port=8000)
3)代码说明
 get_hitokoto 函数
该函数负责发送 HTTP 请求到一言网的 API,获取随机名言。处理请求过程中可能出现的错误,包括请求失败和网络异常。返回一个包含名言和来源信息的字典,或者包含错误信息的字典。
 homepage 函数
作为 Web 服务的根路径处理函数。调用 get_hitokoto 函数获取随机名言,并将结果封装成 JSON 响应返回给客户端。
 Starlette 应用
创建 Starlette 应用实例,并定义路由规则,将根路径 / 映射到 homepage 处理函数。使用 uvicorn 作为ASGI服务器运行应用。
 通过 Starlette 构建 Web 服务,将获取随机名言的功能封装成 API 接口,方便其他应用程序调用。虽然 requests 库是同步的,但 Starlette 本身支持异步处理

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

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

相关文章

Java——pdf增加水印

文章目录 前言方式一 itextpdf项目依赖引入编写PDF添加水印工具类测试效果展示 方式二 pdfbox依赖引入编写实现类效果展示 扩展1、将inputstream流信息添加水印并导出zip2、部署出现找不到指定字体文件 资料参考 前言 近期为了知识库文件导出&#xff0c;文件数据安全处理&…

leetcode_19. 删除链表的倒数第 N 个结点_java

19. 删除链表的倒数第 N 个结点https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 1、题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#…

41、web前端开发之Vue3保姆教程(五 实战案例)

一、项目简介和需求概述 1、项目目标 1.能够基于Vue3创建项目 2.能够基本Vue3相关的技术栈进行项目开发 3.能够使用Vue的第三方组件进行项目开发 4.能够理解前后端分离的开发模式 2、项目概述 使用Vue3结合ElementPlus,ECharts工具实现后台管理系统页面,包含登录功能,…

zsh: command not found: hdc - 鸿蒙 HarmonyOS Next

终端中执行 hdc 命令抛出如下错误; zsh: command not found: hdc 解决办法 首先,查找到 DevEco-Studio 的 toolchains 目录路径; 其次,按照类似如下的文件夹层级结果推理到 toolchains 子级路径下,其中 sdk 后一级的路径可能会存在差异,以实际本地路径结构为主,直至找到 ope…

蓝桥杯--寻找整数

题解 public static void main(String[] args) {int[] mod {0, 0, 1, 2, 1, 4, 5, 4, 1, 2, 9, 0, 5, 10, 11, 14, 9, 0, 11, 18, 9, 11, 11, 15, 17, 9, 23, 20, 25, 16, 29, 27, 25, 11, 17, 4, 29, 22, 37, 23, 9, 1, 11, 11, 33, 29, 15, 5, 41, 46};long t lcm(2, 3);lo…

自然语言处理入门6——RNN生成文本

一、文本生成 我们在前面的文章中介绍了LSTM&#xff0c;根据输入时序数据可以输出下一个可能性最高的数据&#xff0c;如果应用在文字上&#xff0c;就是根据输入的文字&#xff0c;可以预测下一个可能性最高的文字。利用这个特点&#xff0c;我们可以用LSTM来生成文本。输入…

FPGA_DDR错误总结

1otp 31-67 解决 端口没连接 必须赋值&#xff1b; 2.PLACE 30-58 TERM PLINITCALIBZ这里有问题 在顶层输出但是没有管脚约束报错 3.ERROR: [Place 30-675] 这是时钟不匹配IBUF不在同一个时钟域&#xff0c;时钟不在同一个时钟域里&#xff0c;推荐的不建议修改 问题 原本…

NOIP2011提高组.玛雅游戏

目录 题目算法标签: 模拟, 搜索, d f s dfs dfs, 剪枝优化思路*详细注释版代码精简注释版代码 题目 185. 玛雅游戏 算法标签: 模拟, 搜索, d f s dfs dfs, 剪枝优化 思路 可行性剪枝 如果某个颜色的格子数量少于 3 3 3一定无解因为要求字典序最小, 因此当一个格子左边有…

基于ssm框架的校园代购服务订单管理系统【附源码】

1、系统框架 1.1、项目所用到技术&#xff1a; javaee项目 Spring&#xff0c;springMVC&#xff0c;mybatis&#xff0c;mvc&#xff0c;vue&#xff0c;maven项目。 1.2、项目用到的环境&#xff1a; 数据库 &#xff1a;mysql5.X、mysql8.X都可以jdk1.8tomcat8 及以上开发…

【10】数据结构的矩阵与广义表篇章

目录标题 二维以上矩阵矩阵存储方式行序优先存储列序优先存储 特殊矩阵对称矩阵稀疏矩阵三元组方式存储稀疏矩阵的实现三元组初始化稀疏矩阵的初始化稀疏矩阵的创建展示当前稀疏矩阵稀疏矩阵的转置 三元组稀疏矩阵的调试与总代码十字链表方式存储稀疏矩阵的实现十字链表数据标签…

猜猜乐游戏(python)

import randomprint(**30) print(欢迎进入娱乐城) print(**30)username input(输入用户名&#xff1a;) cs 0answer input( 是否加入"猜猜乐"游戏(yes/no)? )if answer yes:while True:num int(input(%s! 当前你的金币数为%d! 请充值(100&#xffe5;30币&…

spring boot 2.7 集成 Swagger 3.0 API文档工具

背景 Swagger 3.0 是 OpenAPI 规范体系下的重要版本&#xff0c;其前身是 Swagger 2.0。在 Swagger 2.0 之后&#xff0c;该规范正式更名为 OpenAPI 规范&#xff0c;并基于新的版本体系进行迭代&#xff0c;因此 Swagger 3.0 实际对应 OpenAPI 3.0 版本。这一版本着重强化了对…

Dinky 和 Flink CDC 在实时整库同步的探索之路

摘要&#xff1a;本文整理自 Dinky 社区负责人&#xff0c;Apache Flink CDC contributor 亓文凯老师在 Flink Forward Asia 2024 数据集成&#xff08;二&#xff09;专场中的分享。主要讲述 Dinky 的整库同步技术方案演变至 Flink CDC Yaml 作业的探索历程&#xff0c;并深入…

视频融合平台EasyCVR搭建智慧粮仓系统:为粮仓管理赋能新优势

一、项目背景 当前粮仓管理大多仍处于原始人力监管或初步信息化监管阶段。部分地区虽采用了简单的传感监测设备&#xff0c;仍需大量人力的配合&#xff0c;这不仅难以全面监控粮仓复杂的环境&#xff0c;还容易出现管理 “盲区”&#xff0c;无法实现精细化的管理。而一套先进…

3D Gaussian Splatting as MCMC 与gsplat中的应用实现

3D高斯泼溅(3D Gaussian splatting)自2023年提出以后,相关研究paper井喷式增长,尽管出现了许多改进版本,但依旧面临着诸多挑战,例如实现照片级真实感、应对高存储需求,而 “悬浮的高斯核” 问题就是其中之一。浮动高斯核通常由输入图像中的曝光或颜色不一致引发,也可能…

C++初阶-C++的讲解1

目录 1.缺省(sheng)参数 2.函数重载 3.引用 3.1引用的概念和定义 3.2引用的特性 3.3引用的使用 3.4const引用 3.5.指针和引用的关系 4.nullptr 5.总结 1.缺省(sheng)参数 &#xff08;1&#xff09;缺省参数是声明或定义是为函数的参数指定一个缺省值。在调用该函数是…

STM32_USB

概述 本文是使用HAL库的USB驱动 因为官方cubeMX生成的hal库做组合设备时过于繁琐 所以这里使用某大神的插件,可以集成在cubeMX里自动生成组合设备 有小bug会覆盖生成文件里自己写的内容,所以生成一次后注意保存 插件安装 下载地址 https://github.com/alambe94/I-CUBE-USBD-Com…

STM32 的编程方式总结

&#x1f9f1; 按照“是否可独立工作”来分&#xff1a; 库/方式是否可独立使用是否依赖其他库说明寄存器裸写✅ 是❌ 无完全自主控制&#xff0c;无库依赖标准库&#xff08;StdPeriph&#xff09;✅ 是❌ 只依赖 CMSIS自成体系&#xff08;F1专属&#xff09;&#xff0c;只…

MFC工具栏CToolBar从专家到小白

CToolBar m_wndTool; //创建控件 m_wndTool.CreateEx(this, TBSTYLE_FLAT|TBSTYLE_NOPREFIX, WS_CHILD | WS_VISIBLE | CBRS_FLYBY | CBRS_TOP | CBRS_SIZE_DYNAMIC); //加载工具栏资源 m_wndTool.LoadToolBar(IDR_TOOL_LOAD) //在.rc中定义&#xff1a;IDR_TOOL_LOAD BITMAP …

大厂机考——各算法与数据结构详解

目录及其索引 哈希双指针滑动窗口子串普通数组矩阵链表二叉树图论回溯二分查找栈堆贪心算法动态规划多维动态规划学科领域与联系总结​​ 哈希 ​​学科领域​​&#xff1a;计算机科学、密码学、数据结构 ​​定义​​&#xff1a;通过哈希函数将任意长度的输入映射为固定长度…