例えばTodoリストや予約機能。複数タスクを直近順に並べたいことがある。
こんなときはヒープキューとクラスの特殊メソッドを用いると上手くいく。
ヒープキュー
標準ライブラリheapq
を活用する。優先度付きキューを生成することができ木構造によって管理されるため、直近タスクの取り出しを容易に行うことができる。
優先度の判定は要素ごとの比較演算、タプルの場合は第1引数が参照される。また、仕様上ヒープキューの第1要素が必ず最小要素となる。
import heapq as hq heap = [] # heapに要素を追加 hq.heappush(heap, 10) hq.heappush(heap, 1) hq.heappush(heap, 5) # heapから要素を取り出し x = hq.heappop(heap) # x = 1 x = hq.heappop(heap) # x = 5 x = hq.heappop(heap) # x = 10
クラスの特殊メソッド
特殊メソッド__lt__
を実装することでクラス間の比較演算を行うことができる。
class User:
def __init__(self, value):
self.value = value
def __lt__(self, other):
if not isinstance(other, User):
return NotImplemented
return self.value < other.value
上の例はUserクラスをインスタンス変数valueで比較演算できるようにしている。これで以下のように扱えるようになる。
a = User(10) b = User(15) print(a < b) # -> True
用例
複数タスクを保持し、直近のタスクから取り出して処理したい場合を考える。
今回は各タスクは情報として、
- タスク名:name
- 実行時刻:datetime
を持っていると仮定する。
実行時刻が近い順にタスクを参照したいのでヒープキューを用い、比較演算できない要素はヒープキューに挿入できないので特殊メソッドを実装する方針。
import datetime
import heapq as hq
# タスクの情報を保持するクラス
class Task:
# 本来は引数要素の型チェックを行うが省略
def __init__(self, name, datetime):
self.name = name
self.datetime = datetime
def __lt__(self, other):
if not isinstance(other, Task):
return NotImplemented
return self.datetime < other.datetime
tasks = []
# タスクの追加
hq.heappush(tasks, Task("タスクA", datetime.datetime(2021, 4, 1)))
hq.heappush(tasks, Task("タスクA", datetime.datetime(2021, 6, 1)))
hq.heappush(tasks, Task("タスクA", datetime.datetime(2021, 5, 1)))
# タスクが存在する間ループ
while tasks:
# タスクを取り出したくない場合はtask[0]を参照すればよい
# 実行時刻を超えたらタスクをキューから取り出して処理する
if tasks[0] <= datetime.datetime.now():
task = hq.heappop(tasks)
# タスクに対する何らかの処理
# スリープ処理等
コメント