如何在Python中创建带优先级的基于时间的任务队列?
嘿,这个需求很实际——既要按优先级调度任务,还要处理同时间段的高优先级任务覆盖低优先级的情况。既然你已经有了基础的优先级队列实现,咱们可以在这个基础上扩展时间冲突的检查和处理逻辑,下面是具体的实现思路和代码示例:
实现思路与代码示例
1. 定义可比较的任务结构
首先,我们需要把任务的时间范围、优先级、ID等信息封装成一个类,并且实现时间重叠检查和优先级比较的逻辑。这里用Python的dataclasses来简化类的定义:
from dataclasses import dataclass from datetime import datetime import queue @dataclass(order=True) class Job: priority: int # 数值越小优先级越高(和你的示例一致) start_time: datetime end_time: datetime job_id: str def overlaps_with(self, other: 'Job') -> bool: """检查两个任务是否在同一天且时间段重叠""" if self.start_time.date() != other.start_time.date(): return False # 时间段重叠的条件:A开始时间 < B结束时间 且 A结束时间 > B开始时间 return self.start_time < other.end_time and self.end_time > other.start_time
注意:order=True会让Job类自动支持比较操作,PriorityQueue会根据这个比较结果进行堆排序,优先级数值越小的任务会排在前面。
2. 自定义带时间冲突处理的优先级队列
原生的PriorityQueue只负责按优先级排序,不会处理时间冲突,所以我们需要封装一个自定义队列,在添加任务时先检查冲突,再决定是否替换旧任务:
class TimePriorityQueue: def __init__(self): self._queue = queue.PriorityQueue() # 维护一个列表跟踪所有已添加的任务,方便冲突检查 self._jobs = [] def put(self, job: Job): # 先检查是否有冲突的任务 conflicting_jobs = [j for j in self._jobs if job.overlaps_with(j)] should_add = True for existing_job in conflicting_jobs: if job.priority < existing_job.priority: # 新任务优先级更高,移除旧任务 self._remove_job(existing_job) else: # 新任务优先级更低,直接放弃添加 should_add = False if should_add: self._jobs.append(job) self._queue.put(job) def get(self) -> Job: job = self._queue.get() self._jobs.remove(job) return job def empty(self) -> bool: return self._queue.empty() def _remove_job(self, job_to_remove: Job): """从队列中移除指定任务(处理堆结构的局限性)""" # 原生PriorityQueue是堆结构,无法直接删除元素,所以先取出所有元素过滤后重新放入 temp_jobs = [] while not self._queue.empty(): current_job = self._queue.get() if current_job != job_to_remove: temp_jobs.append(current_job) # 重新构建堆 for job in temp_jobs: self._queue.put(job) # 从跟踪列表中移除 self._jobs.remove(job_to_remove)
3. 测试你的示例场景
接下来我们用你给出的示例任务来测试这个队列:
def parse_time_string(time_str: str) -> tuple[datetime, datetime]: """解析"13:00-14:00 5.5.2018"格式的时间字符串为datetime对象""" time_range, date_part = time_str.split() start_time_str, end_time_str = time_range.split('-') day, month, year = map(int, date_part.split('.')) start_hour, start_minute = map(int, start_time_str.split(':')) end_hour, end_minute = map(int, end_time_str.split(':')) start_datetime = datetime(year, month, day, start_hour, start_minute) end_datetime = datetime(year, month, day, end_hour, end_minute) return start_datetime, end_datetime # 创建示例任务 job1_start, job1_end = parse_time_string("13:00-14:00 5.5.2018") job1 = Job(priority=10, start_time=job1_start, end_time=job1_end, job_id="Job 1") job2_start, job2_end = parse_time_string("14:00-15:00 5.5.2018") job2 = Job(priority=10, start_time=job2_start, end_time=job2_end, job_id="Job 2") job3_start, job3_end = parse_time_string("14:00-15:00 5.5.2018") job3 = Job(priority=1, start_time=job3_start, end_time=job3_end, job_id="Job 3") # 测试队列 tpq = TimePriorityQueue() tpq.put(job1) tpq.put(job2) tpq.put(job3) # 依次取出任务,验证结果 print("任务取出顺序:") while not tpq.empty(): job = tpq.get() time_format = job.start_time.strftime("%H:%M-%H:%M %d.%m.%Y") print(f"- {job.job_id} | 优先级{job.priority} | 时间{time_format}")
运行这段代码,你会看到输出是:
任务取出顺序: - Job 3 | 优先级1 | 时间14:00-15:00 05.05.2018 - Job 1 | 优先级10 | 时间13:00-14:00 05.05.2018
完全符合你的需求:Job3覆盖了同时间段的Job2,最终队列里只有Job3和Job1,且优先级高的Job3先被取出。
4. 关键逻辑说明
- 时间重叠判断:
overlaps_with方法是核心,确保只有同一天且时间段有重叠的任务才会触发覆盖逻辑。 - 冲突处理:添加新任务时,会先检查所有现有任务,若新任务优先级更高,则移除旧任务;若优先级更低,则直接跳过添加。
- 堆结构的局限性:原生
PriorityQueue基于堆实现,不支持高效删除元素,所以我们采用了“取出所有元素过滤后重新放入”的方式。如果你的任务量很大,可以考虑用heapq模块自行维护堆结构,实现更高效的删除操作。
内容的提问来源于stack exchange,提问作者emir
相关产品推荐
相关产品推荐

