一、queue.Queue:线程安全队列
queue.Queue主要用于多个线程之间安全地传递任务。
1. 导入Queue
from queue import Queue
2. 创建队列
python
task_queue = Queue()
限制最大容量:
python
task_queue = Queue(maxsize=100)
maxsize=100表示队列最多保存100个尚未取出的元素。
3. 放入元素
python
task_queue.put("任务1")
task_queue.put("任务2")
put()用于把元素加入队列。
4. 取出元素
task = task_queue.get()
get()会取出最早加入的元素。
如果队列为空,默认情况下get()会阻塞等待新任务。
5. 标记任务完成
task_queue.task_done()
这表示通过get()取出的一个任务已经处理完成。
等待所有任务处理完毕:
task_queue.join()
join()会等待,直到所有加入队列的任务都已经调用对应的task_done()。
6. 其他队列类型
python
from queue import Queue, LifoQueue, PriorityQueue
三种类型分别为:
Queue:先进先出;LifoQueue:后进先出;PriorityQueue:优先级队列。
优先级队列示例:
python
from queue import PriorityQueue
priority_queue = PriorityQueue()
priority_queue.put((2, "普通任务"))
priority_queue.put((1, "紧急任务"))
task = priority_queue.get()
print(task)
输出:
python
(1, "紧急任务")
元组会先比较第一个元素,因此数字更小的任务会先取出。
二、asyncio.Queue:异步队列
asyncio.Queue用于多个协程之间传递数据。
1. 完整示例
python
import asyncio
async def producer(queue: asyncio.Queue) -> None:
await queue.put("工具执行结果")
async def consumer(queue: asyncio.Queue) -> None:
result = await queue.get()
print(result)
queue.task_done()
async def main() -> None:
queue = asyncio.Queue()
await asyncio.gather(
producer(queue),
consumer(queue),
)
asyncio.run(main())
2. async def是什么
python
async def producer(queue: asyncio.Queue) -> None:
解释:
async def用于定义协程函数;producer是函数名称;queue是函数参数;:asyncio.Queue是参数类型注解;-> None是返回值类型注解;- 行末的
:表示开始函数代码块。
类型注解主要用于:
- 增强代码可读性;
- 为IDE提供提示;
- 配合静态类型检查工具;
- 帮助框架读取参数信息。
普通类型注解默认不会自动完成运行时校验。
3. await是什么
python
await queue.put("工具执行结果")
await表示等待一个可等待对象完成。
等待期间,当前协程可以暂时让出执行权,让事件循环执行其他协程。
从队列中获取元素:
python
result = await queue.get()
如果队列为空,消费者协程会暂停等待,但不会像普通阻塞代码那样阻塞整个事件循环。
4. asyncio.gather是什么
python
await asyncio.gather(
producer(queue),
consumer(queue),
)
gather()接收多个可等待对象,并发等待它们完成。这里生产者和消费者都会被提交给事件循环调度。
5. asyncio.run是什么
python
asyncio.run(main())
它会:
- 创建事件循环;
- 运行
main()协程;- 等待
main()完成;- 清理异步任务;
- 关闭事件循环。
在普通Python脚本中,它通常是进入异步代码的入口。
三、array:固定类型数组
array用于保存类型相同的基础数据。
1. 导入array
from array import array
2. 创建整数数组
numbers = array("i", [10, 20, 30])
解释:
array是数组类型;- 第一个参数
"i"是类型码;"i"通常表示有符号整数;- 第二个参数是初始数据。
3. 添加元素
numbers.append(40)
因为数组要求元素类型统一,下面代码会报错:
numbers.append("hello")
普通业务开发中,大多数情况下使用list即可。当需要紧凑保存大量相同基础类型时,可以考虑array。
四、三种队列应该如何选择
| 类型 | 主要使用场景 |
|---|---|
collections.deque |
普通双端队列、滑动窗口、最近记录 |
queue.Queue |
多线程之间传递任务 |
asyncio.Queue |
多协程之间传递任务 |
普通同步代码:
python
from collections import deque
queue = deque()
queue.append("任务")
task = queue.popleft()
多线程代码:
python
from queue import Queue
queue = Queue()
queue.put("任务")
task = queue.get()
异步代码:
python
import asyncio
queue = asyncio.Queue()
await queue.put("任务")
task = await queue.get()
最后一段代码中的await需要出现在协程函数等允许使用异步语法的环境中。
五、综合案例:统计工具调用结果
python
from collections import Counter, defaultdict, deque
import heapq
records = [
{
"tool": "news_search",
"status": "success",
"latency_ms": 120,
},
{
"tool": "market_analysis",
"status": "success",
"latency_ms": 180,
},
{
"tool": "quotation_search",
"status": "timeout",
"latency_ms": 500,
},
{
"tool": "news_search",
"status": "success",
"latency_ms": 100,
},
{
"tool": "enterprise_search",
"status": "error",
"latency_ms": 80,
},
]
1. 统计状态出现次数
status_counter = Counter(
record["status"]
for record in records
)
下面这一部分:
record["status"] for record in records
是生成器表达式。
它会:
- 遍历
records;- 每次把当前记录保存到
record;- 读取
record["status"];- 逐个把状态提供给
Counter。
2. 按工具名称分组
python
records_by_tool = defaultdict(list)
for record in records:
tool_name = record["tool"]
records_by_tool[tool_name].append(record)
第一次访问某个工具名称时:
records_by_tool[tool_name]
defaultdict会自动调用:
list()
创建一个新的空列表。随后:
.append(record)
把当前记录加入该工具对应的列表。
3. 保存最近三条记录
recent_records = deque(
records,
maxlen=3,
)
这里:
records是初始数据;maxlen=3表示最多保留三条;- 当数据超过三条时,只保留最后三条。
4. 找到最慢的两次调用
slowest_records = heapq.nlargest(
2,
records,
key=lambda record: record["latency_ms"],
)
它会根据每条记录的latency_ms字段进行比较,并返回耗时最大的两条记录。
5. 输出结果
print("状态统计:", status_counter)
print("按工具分组:", dict(records_by_tool))
print("最近三条:", list(recent_records))
print("最慢两条:", slowest_records)
这里使用:
dict(records_by_tool)
把defaultdict转换成普通字典,方便打印或序列化。使用:
list(recent_records)
把deque转换成普通列表。
六、常见错误总结
1. 把append返回值赋给列表
错误:
numbers = numbers.append(10)
正确:
numbers.append(10)
append()直接修改原列表,返回值为None。
2. 使用空大括号创建集合
错误:
values = {}
这会创建字典。
正确:
values = set()
3. 使用list.pop(0)实现高频队列
不推荐:
task = tasks.pop(0)
推荐:
from collections import deque
tasks = deque()
task = tasks.popleft()
4. 给defaultdict传入list()
错误:
groups = defaultdict(list())
正确:
groups = defaultdict(list)
defaultdict需要一个可以稍后调用的默认工厂。
5. 认为heapify完成了整体排序
heapq.heapify(numbers)
只保证堆性质,不保证列表完全有序。需要完整排序时使用:
sorted_numbers = sorted(numbers)
6. 在普通函数中使用await
错误:
def get_task(queue):
task = await queue.get()
正确:
async def get_task(queue):
task = await queue.get()
await通常只能出现在通过async def定义的协程函数中。
十六、容器快速选择表
| 需求 | 推荐工具 |
|---|---|
| 保存有顺序、可修改的数据 | list |
| 保存有顺序、创建后不修改的数据 | tuple |
| 根据键查找值 | dict |
| 去重、集合运算 | set |
| 在两端高效添加和删除 | deque |
| 为缺失键自动创建默认值 | defaultdict |
| 统计元素出现次数 | Counter |
| 获取最小值、最大值或Top-K | heapq |
| 在线程之间传递任务 | queue.Queue |
| 在协程之间传递任务 | asyncio.Queue |
| 保存相同基础类型的数据 | array |
总结
Python最常用的四个内置容器是:
python
list
tuple
dict
set
标准库中常用的扩展容器和工具包括:
python
deque
defaultdict
Counter
heapq
queue.Queue
asyncio.Queue
array
学习一个容器时,不能只记住方法名称,还需要理解:
- 容器是否保留顺序;
- 容器是否可以修改;
- 是否允许重复元素;
- 元素是否需要可哈希;
- 查询、插入和删除的大致时间复杂度;
- 容器是否支持线程或协程场景;
- 方法是直接修改原对象,还是返回新对象;
- 方法的返回值是什么。
掌握这些容器后,下一篇我们就可以继续学习Python函数、类、装饰器、迭代器、生成器、asyncio和FastAPI后端开发。