1. 写在最前面
最近加了很多 Python Coding 的任务,虽然在 AI 加持下能够顺利完成,但是还是觉得心理不踏实,觉得很多代码 AI 写完自己不是很懂,不喜欢这种感觉。
刚好周日上午没事,抽空记录在 Python 开发中遇到的神奇语法和库。
2. Python 语法
2.1 yield
修改的代码中,函数的返回很多处用了 Yield 而不是 return ,这引起了我的好奇。
2.1.1 yield 和 return 的区别
在 Python 中,yield
和 return
都用于从函数中返回值,但它们之间有一些重要的区别:
-
返回类型
-
return
:return
语句用于结束函数的执行,并将一个值返回给调用者。函数在执行到return
时会立即退出。- 每次调用函数时,都是从头开始执行,直到遇到
return
。
-
yield
:yield
语句用于定义生成器函数。生成器函数在执行时不会立即返回值,而是返回一个生成器对象。- 当生成器函数被调用时,它不会立即执行,而是返回一个生成器对象。可以通过迭代这个生成器对象来逐步执行函数的代码,每次遇到
yield
时返回一个值,并在下一次迭代时从上次停止的地方继续执行。
-
-
内存使用
-
return
:- 返回一个完整的结果(例如一个列表),可能会占用较多内存,特别是当结果很大时。
-
yield
:- 生成器按需生成值,通常会更节省内存,特别是在处理大型数据集时,因为它不会一次性生成所有结果。
-
-
yield 的使用场景:
- 内存效率:适合处理大数据集,避免一次性加载。
- 无限序列:可生成无限序列,按需计算。
- 状态保持:在函数中保持状态,适合需要维护状态的场景。
- 协程:在异步编程中用于协作式多任务。
- 管道处理:构建数据处理管道,方便数据流动。
-
示例
-
使用
return
的函数:pythondef get_squares(n): return [x*x for x in range(n)] squares = get_squares(5) print(squares) # 输出: [0, 1, 4, 9, 16]
-
使用
yield
的生成器:pythondef get_squares(n): for x in range(n): yield x*x squares_gen = get_squares(5) for square in squares_gen: print(square) # 输出: 0, 1, 4, 9, 16
-
2.1.2 golang 中实现 yield 语法
之前写的是 golang ,为了用类比法更好的理解 yield ,可以在 golang 中实现一个类似能力的示例。
go
package main
import (
"fmt"
)
// 生成器函数,返回一个通道
func getSquares(n int) <-chan int {
ch := make(chan int) // 创建一个通道
go func() {
for x := 0; x < n; x++ {
ch <- x * x // 将计算结果发送到通道
}
close(ch) // 关闭通道,表示没有更多值
}()
return ch // 返回通道
}
func main() {
squares := getSquares(5) // 获取生成器通道
for square := range squares { // 迭代通道中的值
fmt.Println(square) // 输出: 0, 1, 4, 9, 16
}
}
函数说明:
- 通道 :在
getSquares
函数中,我们创建了一个通道ch
,用于发送计算结果。 - goroutine :我们使用
go
关键字启动了一个 goroutine,在这个 goroutine 中计算平方并将结果发送到通道。 - 关闭通道:当所有值都发送完后,我们关闭通道,以便接收方知道没有更多值可供接收。
- 迭代通道 :在
main
函数中,我们使用range
迭代通道,从中接收值。
3. aiohttp 库
需求是需要在客户请求大模型前,提前发送一次请求大模型,确保在客户请求的时候,就可以节省掉 tls 握手和 tcp 建立连接的时间,简称之为预热。
3.1 原始写法
python
async def analyze_backend_consistency(num_requests: int = 5, delay: float = 0.5, concurrent: bool = False):
# 顺序发送请求
results = []
for i in range(num_requests):
try:
connector = aiohttp.TCPConnector(ssl=True)
async with aiohttp.ClientSession(connector=connector) as session:
result = await make_request(session, i + 1)
results.append(result)
if i < num_requests - 1:
await asyncio.sleep(delay)
except Exception as e:
print(f"请求 {i + 1} 失败: {str(e)}")
# 打印每个请求的结果
for result in results:
print(f"\n请求 {result['request_id']} 结果:")
print(f"远程IP:端口 = {result['remote_ip']}:{result['remote_port']}")
print(f"请求ID = {result['x_request_id']}")
print(f"处理时间 = {result['upstream_time']}ms")
print(f"总响应时间 = {result['response_time']}ms")
print("-" * 50)
# 分析结果
print("\n分析结果:")
unique_ips = set(r['remote_ip'] for r in results)
print(f"使用的不同IP数量: {len(unique_ips)}")
print(f"IP列表: {', '.join(str(ip) for ip in unique_ips)}")
# 计算平均响应时间
avg_response_time = sum(float(r['response_time']) for r in results) / len(results)
print(f"平均响应时间: {avg_response_time:.2f}ms")
# 检查是否所有请求都使用了同一个连接
print(f"所有请求是否使用同一个连接: {len(unique_ips) == 1}")
# 分析处理时间
upstream_times = [float(r['upstream_time']) for r in results]
print(f"后端处理时间范围: {min(upstream_times):.2f}ms - {max(upstream_times):.2f}ms")
print(f"后端处理时间平均值: {sum(upstream_times)/len(upstream_times):.2f}ms")
测试结果:

3.2 修改写法
python
async def analyze_backend_consistency(num_requests: int = 5, delay: float = 0.5, concurrent: bool = False):
connector = aiohttp.TCPConnector(ssl=True)
async with aiohttp.ClientSession(connector=connector) as session:
results = []
if concurrent:
# 并发发送所有请求
tasks = [make_request(session, i + 1) for i in range(num_requests)]
results = await asyncio.gather(*tasks)
else:
# 顺序发送请求
for i in range(num_requests):
try:
result = await make_request(session, i + 1)
results.append(result)
if i < num_requests - 1:
await asyncio.sleep(delay)
except Exception as e:
print(f"请求 {i + 1} 失败: {str(e)}")
# 打印每个请求的结果
for result in results:
print(f"\n请求 {result['request_id']} 结果:")
print(f"远程IP:端口 = {result['remote_ip']}:{result['remote_port']}")
print(f"请求ID = {result['x_request_id']}")
print(f"处理时间 = {result['upstream_time']}ms")
print(f"总响应时间 = {result['response_time']}ms")
print("-" * 50)
# 分析结果
print("\n分析结果:")
unique_ips = set(r['remote_ip'] for r in results)
print(f"使用的不同IP数量: {len(unique_ips)}")
print(f"IP列表: {', '.join(str(ip) for ip in unique_ips)}")
# 计算平均响应时间
avg_response_time = sum(float(r['response_time']) for r in results) / len(results)
print(f"平均响应时间: {avg_response_time:.2f}ms")
# 检查是否所有请求都使用了同一个连接
print(f"所有请求是否使用同一个连接: {len(unique_ips) == 1}")
# 分析处理时间
upstream_times = [float(r['upstream_time']) for r in results]
print(f"后端处理时间范围: {min(upstream_times):.2f}ms - {max(upstream_times):.2f}ms")
print(f"后端处理时间平均值: {sum(upstream_times)/len(upstream_times):.2f}ms")
测试结果:

3.2 耗时对比分析
原始写法 | 修改写法 | |
---|---|---|
特点 | 每个请求都创建新的 TCPConnector 和 ClientSession 每次请求都要重新建立 TCP 连接和 TLS 握手 不复用 HTTP/2 连接 每个请求的耗时 = TCP建立(40ms) + TLS握手(200ms) + 请求处理时间 | 只创建一次 TCPConnector 和 ClientSession TCP 连接和 TLS 握手只进行一次 复用 HTTP/2 连接 第一个请求耗时 = TCP建立 + TLS握手 + 请求处理时间 后续请求耗时 = 请求处理时间 |
4. 碎碎念
周日的时候写了 80%,今晚刚好手里的活搞的差不多了,给总结收个尾。上海最近的暴雨和雷声有点子吓人:
- 世间最重要的事莫过于懂得让自己属于自己。
- 偶尔觉得妈妈很丢人,妈妈为什么连起码的脸面的自尊都没有呢?我都觉得上火。比起她自己,她有更想守护的,那就是我。人真正变强大,不是因为守护着自尊心,而是抛开自尊心的时候。所以妈妈很强大。
这周就要回去看妈妈啦,开心,就写到这里吧。