読み込み中...

Pythonのpopleftを利用したスタックとキューの作成・活用10選

popleft 徹底解説 Python
この記事は約32分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●Pythonのpopleftとは?

Pythonには様々なデータ構造がありますが、その中でも特に注目すべき機能の1つがpopleftメソッドです。

popleftは、データを素早く取り出す際に大変便利な道具となります。

popleftメソッドは、主にdeque(デック)というデータ構造で使用されます。

dequeは「double-ended queue」の略で、両端キューとも呼ばれます。

両端からデータの追加や削除が可能な、非常に柔軟性の高いデータ構造なのです。

○dequeを使ったpopleftの基本

dequeを使用するには、まずcollectionsモジュールからインポートする必要があります。

次のようにしてdequeオブジェクトを作成し、popleftメソッドを使用できます。

from collections import deque

# dequeオブジェクトの作成
my_deque = deque([1, 2, 3, 4, 5])

# popleftメソッドを使用して最初の要素を取り出す
first_element = my_deque.popleft()

print(f"取り出された要素: {first_element}")
print(f"残りのdeque: {my_deque}")

実行結果

取り出された要素: 1
残りのdeque: deque([2, 3, 4, 5])

popleftメソッドは、dequeの左端(先頭)から要素を取り出し、その要素を返します。

同時に、その要素はdequeから削除されます。取り出された要素は変数に代入して後で使用することができます。

○リストとdequeにおけるパフォーマンスの違い

Pythonでデータを扱う際、リストとdequeの使い分けは重要です。

特に大量のデータを扱う場合、その違いが顕著になります。

リストの場合、先頭の要素を取り出すためにpop(0)メソッドを使用します。

dequeの場合はpopleftメソッドを使います。

両者の違いを比較するために、簡単なベンチマークテストを実施しましょう。

import time
from collections import deque

# リストとdequeを準備
my_list = list(range(1000000))
my_deque = deque(range(1000000))

# リストのpop(0)の計測
start_time = time.time()
for _ in range(10000):
    my_list.pop(0)
list_time = time.time() - start_time

# dequeのpopleftの計測
start_time = time.time()
for _ in range(10000):
    my_deque.popleft()
deque_time = time.time() - start_time

print(f"リストのpop(0)にかかった時間: {list_time:.6f}秒")
print(f"dequeのpopleftにかかった時間: {deque_time:.6f}秒")
print(f"dequeは{list_time / deque_time:.2f}倍高速")

実行結果

リストのpop(0)にかかった時間: 0.131153秒
dequeのpopleftにかかった時間: 0.001460秒
dequeは89.83倍高速

結果から明らかなように、dequeのpopleftはリストのpop(0)と比べて圧倒的に高速です。

約90倍の速度差があります。

○サンプルコード1:popleftの基本使用法

popleftの基本的な使用法をさらに詳しく見ていきましょう。

次のコードは、dequeを使って簡単なタスク管理システムを実装しています。

from collections import deque

class TaskQueue:
    def __init__(self):
        self.tasks = deque()

    def add_task(self, task):
        self.tasks.append(task)
        print(f"タスク '{task}' を追加しました。")

    def process_next_task(self):
        if self.tasks:
            next_task = self.tasks.popleft()
            print(f"タスク '{next_task}' を処理しています。")
        else:
            print("処理するタスクがありません。")

    def show_remaining_tasks(self):
        print(f"残りのタスク: {list(self.tasks)}")

# タスクキューの使用例
task_queue = TaskQueue()

task_queue.add_task("メール確認")
task_queue.add_task("レポート作成")
task_queue.add_task("会議準備")

task_queue.show_remaining_tasks()

task_queue.process_next_task()
task_queue.process_next_task()

task_queue.show_remaining_tasks()

実行結果

タスク 'メール確認' を追加しました。
タスク 'レポート作成' を追加しました。
タスク '会議準備' を追加しました。
残りのタスク: ['メール確認', 'レポート作成', '会議準備']
タスク 'メール確認' を処理しています。
タスク 'レポート作成' を処理しています。
残りのタスク: ['会議準備']

このサンプルコードでは、dequeを使ってタスクを管理しています。

新しいタスクはappendメソッドで追加され、処理すべきタスクはpopleftメソッドで取り出されます。

popleftメソッドにより、最初に追加されたタスクから順番に処理されるため、タスクの優先順位を維持しながら効率的に管理することができます。

●スタックとキューをPythonで実装

データ構造には、スタックとキューという2つの重要な概念があります。

両者はよく似ていますが、データの出し入れの方法が異なります。Pythonでこの2つの構造を実装する方法を見ていきましょう。

○サンプルコード2:リストを使ったスタックの作成

スタックは「後入れ先出し」(LIFO: Last-In-First-Out)の原則に従うデータ構造です。

新しく追加された要素が最初に取り出されます。

Pythonのリストを使用して簡単にスタックを実装できます。

class Stack:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)
        print(f"'{item}' をスタックに追加しました。")

    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        print("スタックが空です。")

    def peek(self):
        if not self.is_empty():
            return self.items[-1]
        print("スタックが空です。")

    def is_empty(self):
        return len(self.items) == 0

    def size(self):
        return len(self.items)

# スタックの使用例
stack = Stack()

stack.push("赤")
stack.push("青")
stack.push("緑")

print(f"スタックのサイズ: {stack.size()}")
print(f"スタックの一番上: {stack.peek()}")

popped_item = stack.pop()
print(f"取り出された要素: {popped_item}")

print(f"現在のスタック: {stack.items}")

実行結果

'赤' をスタックに追加しました。
'青' をスタックに追加しました。
'緑' をスタックに追加しました。
スタックのサイズ: 3
スタックの一番上: 緑
取り出された要素: 緑
現在のスタック: ['赤', '青']

このコードでは、pushメソッドでスタックに要素を追加し、popメソッドで最後に追加された要素を取り出しています。

peekメソッドは、スタックの一番上の要素を確認するために使用されます。

○サンプルコード3:dequeを使ったキューの実装

キューは「先入れ先出し」(FIFO: First-In-First-Out)の原則に従うデータ構造です。

最初に追加された要素が最初に取り出されます。

Pythonのdequeを使用してキューを実装すると、効率的なデータ操作が可能になります。

from collections import deque

class Queue:
    def __init__(self):
        self.items = deque()

    def enqueue(self, item):
        self.items.append(item)
        print(f"'{item}' をキューに追加しました。")

    def dequeue(self):
        if not self.is_empty():
            return self.items.popleft()
        print("キューが空です。")

    def front(self):
        if not self.is_empty():
            return self.items[0]
        print("キューが空です。")

    def is_empty(self):
        return len(self.items) == 0

    def size(self):
        return len(self.items)

# キューの使用例
queue = Queue()

queue.enqueue("りんご")
queue.enqueue("バナナ")
queue.enqueue("オレンジ")

print(f"キューのサイズ: {queue.size()}")
print(f"キューの先頭: {queue.front()}")

dequeued_item = queue.dequeue()
print(f"取り出された要素: {dequeued_item}")

print(f"現在のキュー: {list(queue.items)}")

実行結果

'りんご' をキューに追加しました。
'バナナ' をキューに追加しました。
'オレンジ' をキューに追加しました。
キューのサイズ: 3
キューの先頭: りんご
取り出された要素: りんご
現在のキュー: ['バナナ', 'オレンジ']

このコードでは、enqueueメソッドでキューに要素を追加し、dequeueメソッドで最初に追加された要素を取り出しています。

frontメソッドは、キューの先頭の要素を確認するために使用されます。

dequeを使用することで、キューの操作が非常に効率的に行えます。

特に、dequeueメソッドでpopleftを使用することで、大量のデータを扱う際にも高速な処理が可能になります。

○サンプルコード4:両端キューの活用例

両端キュー(デック)は、スタックとキューの特性を併せ持つデータ構造です。

両端から要素の追加と削除が可能で、非常に柔軟性が高いです。

Pythonのdequeを使って両端キューを実装してみましょう。

from collections import deque

class Deque:
    def __init__(self):
        self.items = deque()

    def add_front(self, item):
        self.items.appendleft(item)
        print(f"'{item}' を前方に追加しました。")

    def add_rear(self, item):
        self.items.append(item)
        print(f"'{item}' を後方に追加しました。")

    def remove_front(self):
        if not self.is_empty():
            return self.items.popleft()
        print("デックが空です。")

    def remove_rear(self):
        if not self.is_empty():
            return self.items.pop()
        print("デックが空です。")

    def peek_front(self):
        if not self.is_empty():
            return self.items[0]
        print("デックが空です。")

    def peek_rear(self):
        if not self.is_empty():
            return self.items[-1]
        print("デックが空です。")

    def is_empty(self):
        return len(self.items) == 0

    def size(self):
        return len(self.items)

# 両端キューの使用例
deque = Deque()

deque.add_rear("A")
deque.add_front("B")
deque.add_rear("C")
deque.add_front("D")

print(f"デックのサイズ: {deque.size()}")
print(f"デックの前方: {deque.peek_front()}")
print(f"デックの後方: {deque.peek_rear()}")

front_item = deque.remove_front()
rear_item = deque.remove_rear()

print(f"前方から取り出された要素: {front_item}")
print(f"後方から取り出された要素: {rear_item}")

print(f"現在のデック: {list(deque.items)}")

実行結果

'A' を後方に追加しました。
'B' を前方に追加しました。
'C' を後方に追加しました。
'D' を前方に追加しました。
デックのサイズ: 4
デックの前方: D
デックの後方: C
前方から取り出された要素: D
後方から取り出された要素: C
現在のデック: ['B', 'A']

両端キューを使用すると、データの追加と削除を両端から自由に行えます。

例えば、ブラウザの履歴管理やテキストエディタのundo/redo機能の実装など、柔軟なデータ操作が必要な場面で活躍します。

○サンプルコード5:循環キューの実現方法

循環キューは、固定サイズの配列を効率的に使用するデータ構造です。

最後の要素の次が最初の要素になるため、メモリを無駄なく活用できます。

Pythonのdequeを使って循環キューを実装してみましょう。

from collections import deque

class CircularQueue:
    def __init__(self, capacity):
        self.queue = deque(maxlen=capacity)
        self.capacity = capacity

    def enqueue(self, item):
        if self.is_full():
            print("キューが満杯です。最も古い要素を削除します。")
            self.queue.popleft()
        self.queue.append(item)
        print(f"'{item}' をキューに追加しました。")

    def dequeue(self):
        if not self.is_empty():
            return self.queue.popleft()
        print("キューが空です。")

    def front(self):
        if not self.is_empty():
            return self.queue[0]
        print("キューが空です。")

    def rear(self):
        if not self.is_empty():
            return self.queue[-1]
        print("キューが空です。")

    def is_empty(self):
        return len(self.queue) == 0

    def is_full(self):
        return len(self.queue) == self.capacity

    def size(self):
        return len(self.queue)

# 循環キューの使用例
circular_queue = CircularQueue(3)

circular_queue.enqueue("A")
circular_queue.enqueue("B")
circular_queue.enqueue("C")

print(f"キューのサイズ: {circular_queue.size()}")
print(f"キューの前方: {circular_queue.front()}")
print(f"キューの後方: {circular_queue.rear()}")

circular_queue.enqueue("D")

print(f"現在のキュー: {list(circular_queue.queue)}")

dequeued_item = circular_queue.dequeue()
print(f"取り出された要素: {dequeued_item}")

print(f"最終的なキュー: {list(circular_queue.queue)}")

実行結果

'A' をキューに追加しました。
'B' をキューに追加しました。
'C' をキューに追加しました。
キューのサイズ: 3
キューの前方: A
キューの後方: C
キューが満杯です。最も古い要素を削除します。
'D' をキューに追加しました。
現在のキュー: ['B', 'C', 'D']
取り出された要素: B
最終的なキュー: ['C', 'D']

循環キューでは、キューが満杯になると最も古い要素を自動的に削除し、新しい要素のための空間を作ります。

dequeのmaxlenパラメータを使用することで、簡単に循環キューを実現できます。

このデータ構造は、固定サイズのバッファが必要な場面で非常に有用です。

例えば、ストリーミングデータの処理や、最新のN個の要素だけを保持したい場合などに適しています。

循環キューを使用することで、メモリ使用量を抑えつつ、効率的なデータ管理が可能になります。

特に、リアルタイムシステムやデータストリームの処理など、連続的にデータが流れ込む環境で威力を発揮します。

●popleftのパフォーマンス最適化テクニック

プログラミングでは、効率性が王様です。

特に大規模なデータを扱う場面では、ほんの些細な最適化が大きな違いを生み出します。

popleftメソッドはPythonの隠れた宝石とも言える機能で、適切に使用すれば驚くほどのパフォーマンス向上が見込めます。

○大規模データでのpopleftの威力

大量のデータを処理する場面を想像してみてください。

例えば、数百万件のログデータを順番に処理する必要がある場合、どのようなアプローチを取るでしょうか?

従来のリストを使用した方法では、処理に膨大な時間がかかってしまいます。

popleftを活用すれば、データ処理の速度が劇的に向上します。

具体的な例を見てみましょう。

100万件のデータを処理する場合、リストとdequeの性能差を比較します。

import time
from collections import deque

# データの準備
data_list = list(range(1000000))
data_deque = deque(range(1000000))

# リストを使用した処理
start_time = time.time()
while data_list:
    data_list.pop(0)
list_time = time.time() - start_time

# dequeを使用した処理
start_time = time.time()
while data_deque:
    data_deque.popleft()
deque_time = time.time() - start_time

print(f"リストの処理時間: {list_time:.2f}秒")
print(f"dequeの処理時間: {deque_time:.2f}秒")
print(f"dequeは{list_time / deque_time:.2f}倍高速")

実行結果

リストの処理時間: 44.23秒
dequeの処理時間: 0.05秒
dequeは884.60倍高速

驚きの結果です!dequeとpopleftを使用することで、処理速度が約880倍も速くなりました。

大規模データの処理では、popleftの威力が如実に表れます。

○メモリ使用量の削減方法

効率的なデータ処理は速度だけではありません。

メモリ使用量の削減も重要な要素です。

dequeを使用することで、メモリの使用効率も向上します。

dequeは内部的に循環バッファを使用しているため、要素の追加や削除が効率的に行えます。

メモリ使用量を抑えるためのテクニックをいくつか紹介します。

  1. dequeのmaxlenパラメータを設定することで、自動的に古い要素を削除しながら新しい要素を追加できる
  2. 大量のデータを一度にメモリに読み込むのではなく、ジェネレータを使用して必要な分だけデータを生成する
  3. 大きな配列やバイト列を操作する場合、メモリビューを使用してコピーを作成せずにデータにアクセスする

○サンプルコード6:効率的なデータ処理の実装

実際のプロジェクトでpopleftを活用した効率的なデータ処理の例を見てみましょう。

大量のログデータを処理し、直近の1000件のログだけを保持するシステムを考えます。

from collections import deque
import random

class LogProcessor:
    def __init__(self, max_logs=1000):
        self.logs = deque(maxlen=max_logs)

    def add_log(self, log):
        self.logs.append(log)

    def process_logs(self):
        while self.logs:
            log = self.logs.popleft()
            # ログの処理をここで行う
            print(f"処理中: {log}")

# ログデータの生成(シミュレーション)
def generate_logs(n):
    for i in range(n):
        yield f"Log {i}: Event {random.choice(['Error', 'Warning', 'Info'])}"

# LogProcessorの使用例
processor = LogProcessor()

# 10000件のログを生成し、処理する
for log in generate_logs(10000):
    processor.add_log(log)

print(f"保持しているログ数: {len(processor.logs)}")
processor.process_logs()
print(f"処理後のログ数: {len(processor.logs)}")

実行結果(一部抜粋)

保持しているログ数: 1000
処理中: Log 9000: Event Warning
処理中: Log 9001: Event Info
...
処理中: Log 9998: Event Error
処理中: Log 9999: Event Warning
処理後のログ数: 0

この例では、dequeのmaxlenパラメータを使用して最新の1000件のログだけを保持しています。

ジェネレータを使用してログデータを生成することで、メモリ使用量を抑えています。

popleftメソッドを使用してログを効率的に処理しています。

●よくあるエラーと対処法

プログラミングの道は決して平坦ではありません。popleftを使用する際も、いくつかの落とし穴があります。

よくあるエラーとその対処法を見ていきましょう。

○IndexError: deque index out of range

dequeが空の状態でpopleftを呼び出すと、このエラーが発生します。

対処法は簡単です。

popleftを呼び出す前に、dequeが空でないことを確認しましょう。

from collections import deque

my_deque = deque()

# エラーを避ける方法
if my_deque:
    item = my_deque.popleft()
else:
    print("dequeが空です")

# または、try-except文を使用する方法
try:
    item = my_deque.popleft()
except IndexError:
    print("dequeが空です")

○AttributeError: ‘list’ object has no attribute ‘popleft’

リストオブジェクトにpopleftメソッドを呼び出そうとすると、このエラーが発生します。

原因は明白です。

普通のリストにはpopleftメソッドが存在しません。対処法は、リストの代わりにdequeを使用することです。

from collections import deque

# エラーの原因
my_list = [1, 2, 3]
# my_list.popleft()  # ここでAttributeErrorが発生

# 正しい使用方法
my_deque = deque([1, 2, 3])
item = my_deque.popleft()  # 正常に動作

○TypeError: ‘int’ object is not subscriptable

整数オブジェクトをインデックス指定しようとすると、このエラーが発生します。

popleftメソッドを使用する際に、誤って返り値を別の変数と混同してしまった場合などに起こり得ます。

from collections import deque

my_deque = deque([1, 2, 3])

# エラーの原因
item = my_deque.popleft()
# print(item[0])  # ここでTypeErrorが発生

# 正しい使用方法
print(item)  # 単に値を出力

このエラーは、注意深くコードを書くことで簡単に回避できます。

エラーメッセージをよく読み、デバッグの際には慌てず冷静に対処しましょう。

●popleftの実践的応用例

プログラミングの真髄は、理論を実践に移すことにあります。

popleftメソッドは、単なる概念ではなく、実際のプロジェクトで大いに活躍します。

ここからは、popleftを活用した具体的な応用例を見ていきましょう。

各例を通じて、popleftの威力を肌で感じ取ってください。

○サンプルコード7:幅優先探索アルゴリズム

幅優先探索(BFS)は、グラフやツリー構造のデータを探索するアルゴリズムです。

迷路を解くときのように、近い場所から順番に探していく方法です。

popleftを使うと、このアルゴリズムを効率的に実装できます。

from collections import deque

def bfs(graph, start, goal):
    queue = deque([[start]])
    visited = set([start])

    while queue:
        path = queue.popleft()
        node = path[-1]
        if node == goal:
            return path
        for adjacent in graph.get(node, []):
            if adjacent not in visited:
                visited.add(adjacent)
                new_path = list(path)
                new_path.append(adjacent)
                queue.append(new_path)
    return "ゴールに到達できませんでした"

# グラフの定義(隣接リスト形式)
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

start = 'A'
goal = 'F'
result = bfs(graph, start, goal)
print(f"開始点 {start} からゴール {goal} までの経路: {result}")

実行結果

開始点 A からゴール F までの経路: ['A', 'C', 'F']

このコードでは、queueをdequeで実装しています。

popleftメソッドを使用することで、探索すべき次のノードを効率的に取り出しています。

グラフの探索では、処理の順序が重要です。

popleftを使うことで、適切な順序で探索を進められます。

○サンプルコード8:undo機能の実装

テキストエディタやグラフィックソフトなど、多くのアプリケーションにはundo機能が実装されています。

popleftを使うと、このundo機能を簡単に実装できます。

from collections import deque

class TextEditor:
    def __init__(self):
        self.text = ""
        self.undo_stack = deque()
        self.redo_stack = deque()

    def type(self, char):
        self.undo_stack.append(self.text)
        self.text += char
        self.redo_stack.clear()  # 新しい操作が行われたらredoスタックをクリア

    def undo(self):
        if self.undo_stack:
            self.redo_stack.append(self.text)
            self.text = self.undo_stack.pop()

    def redo(self):
        if self.redo_stack:
            self.undo_stack.append(self.text)
            self.text = self.redo_stack.pop()

# TextEditorの使用例
editor = TextEditor()
editor.type('H')
editor.type('e')
editor.type('l')
editor.type('l')
editor.type('o')
print(f"現在のテキスト: {editor.text}")

editor.undo()
editor.undo()
print(f"2回undoした後のテキスト: {editor.text}")

editor.redo()
print(f"1回redoした後のテキスト: {editor.text}")

実行結果

現在のテキスト: Hello
2回undoした後のテキスト: Hel
1回redoした後のテキスト: Hell

このコードでは、undo_stackとredo_stackという2つのdequeを使用しています。

type操作を行うたびに現在の状態をundo_stackに保存し、undo操作時にpopメソッドで直前の状態を取り出します。

redo操作も同様です。

dequeを使用することで、効率的なメモリ使用と高速な操作が可能になります。

○サンプルコード9:リアルタイムデータ分析

リアルタイムデータ分析では、常に最新のデータを保持し、古いデータを効率的に削除する必要があります。

popleftを使用すると、この要件を簡単に満たすことができます。

from collections import deque
import random
import time

class RealTimeAnalyzer:
    def __init__(self, window_size):
        self.data = deque(maxlen=window_size)
        self.window_size = window_size

    def add_data(self, value):
        self.data.append(value)

    def get_average(self):
        return sum(self.data) / len(self.data) if self.data else 0

    def get_max(self):
        return max(self.data) if self.data else None

    def get_min(self):
        return min(self.data) if self.data else None

# RealTimeAnalyzerの使用例
analyzer = RealTimeAnalyzer(window_size=5)

for _ in range(10):
    value = random.randint(1, 100)
    analyzer.add_data(value)
    print(f"追加された値: {value}")
    print(f"現在のウィンドウ: {list(analyzer.data)}")
    print(f"平均: {analyzer.get_average():.2f}")
    print(f"最大値: {analyzer.get_max()}")
    print(f"最小値: {analyzer.get_min()}")
    print("---")
    time.sleep(1)  # 1秒間隔でデータを追加

実行結果(一部抜粋)

追加された値: 73
現在のウィンドウ: [73]
平均: 73.00
最大値: 73
最小値: 73
---
追加された値: 35
現在のウィンドウ: [73, 35]
平均: 54.00
最大値: 73
最小値: 35
---
...
追加された値: 62
現在のウィンドウ: [35, 94, 16, 22, 62]
平均: 45.80
最大値: 94
最小値: 16
---

このコードでは、dequeのmaxlenパラメータを使用して、常に最新の5つのデータポイントのみを保持しています。

新しいデータが追加されると、自動的に古いデータが削除されます。

popleftメソッドは内部的に使用されており、プログラマーが明示的に呼び出す必要はありません。

○サンプルコード10:マルチスレッド対応キュー

マルチスレッドプログラミングでは、スレッド間でデータを安全に共有する必要があります。

Pythonのqueueモジュールは、マルチスレッド環境で安全に使用できるキューを提供しています。

内部的にdequeを使用しているため、高効率な操作が可能です。

import queue
import threading
import time
import random

def producer(q, name):
    for i in range(5):
        item = f"Item {i} from {name}"
        q.put(item)
        print(f"{name} produced {item}")
        time.sleep(random.random())

def consumer(q, name):
    while True:
        try:
            item = q.get(block=False)
            print(f"{name} consumed {item}")
            q.task_done()
            time.sleep(random.random())
        except queue.Empty:
            break

# キューの作成
q = queue.Queue()

# 生産者スレッドの作成
producer_threads = [
    threading.Thread(target=producer, args=(q, f"Producer-{i}"))
    for i in range(3)
]

# 消費者スレッドの作成
consumer_threads = [
    threading.Thread(target=consumer, args=(q, f"Consumer-{i}"))
    for i in range(2)
]

# すべてのスレッドを開始
for t in producer_threads + consumer_threads:
    t.start()

# 生産者スレッドの終了を待つ
for t in producer_threads:
    t.join()

# キューが空になるまで待つ
q.join()

# 消費者スレッドの終了を待つ
for t in consumer_threads:
    t.join()

print("すべての処理が完了しました")

実行結果(実行のたびに異なる可能性があります)

Producer-0 produced Item 0 from Producer-0
Producer-1 produced Item 0 from Producer-1
Consumer-0 consumed Item 0 from Producer-0
Producer-2 produced Item 0 from Producer-2
Consumer-1 consumed Item 0 from Producer-1
Consumer-0 consumed Item 0 from Producer-2
Producer-1 produced Item 1 from Producer-1
Consumer-1 consumed Item 1 from Producer-1
Producer-0 produced Item 1 from Producer-0
Consumer-0 consumed Item 1 from Producer-0
...
Producer-2 produced Item 4 from Producer-2
Consumer-1 consumed Item 4 from Producer-2
すべての処理が完了しました

このコードでは、queue.Queueクラスを使用してマルチスレッド対応のキューを作成しています。

生産者スレッドはputメソッドでアイテムを追加し、消費者スレッドはgetメソッドでアイテムを取り出しています。

内部的にはdequeが使用されているため、効率的なデータの出し入れが可能です。

まとめ

今回紹介した実践的な応用例を通じて、popleftの有用性を実感していただけたでしょうか。

学んだ知識を活かし、より効率的で洗練されたコードを書くことができるはずです。

実際のプロジェクトでpopleftを使ってみることで、理解がさらに深まり、新たな可能性が見えてくるでしょう。