Pythonでキュー(queue)を扱う方法を知りたい
そもそもデータ構造って何?
Pythonでマルチスレッドを扱う方法が知りたい
皆さんはキューやスタックなどのデータ構造については知っていますか?今回はPythonでキューやスタックを扱う方法について解説していきます!
この記事では、
- データ構造とは
- キューとは
- スタックとは
- queueモジュールの使い方(FIFO、LIFO)
- queueモジュールでマルチスレッドを扱う方法
などの基本的な内容から解説していきます。データ構造についてもこの記事で解説していくのでぜひご覧ください!
本記事を読む前に、Pythonがどんなプログラミング言語なのかをおさらいしておきたい人は次の記事を参考にしてください。
→ Pythonとは?特徴やできること、活用例をわかりやすく簡単に解説
なお、その他のPythonの記事についてはこちらにまとめています。
Pythonのqueueモジュールとは
データ構造とは
キューについて知る前に、まずはデータ構造について知っておきましょう。データ構造は、データの集まりをコンピュータで効率よく扱う仕組みのことを言います。
ソフトウェアの開発ではどのデータ構造を適切な場面で使えるかによって使い勝手や性能が大きく変わってきます。そのデータ構造の一つに、キューやスタックと呼ばれるものがあります。
キューとは
キューは基本的なデータ構造の一つです。
コンビニやデパートなどでレジに並ぶときを思い出してみてください。先に並んだ人が先に会計をしてもらい、後から来た人はその人の後ろに続いて…と言う構造になってますよね。
この先入れ先出し、First In First Out(FIFO)の構造を、キューと呼びます。このキューにデータを入れていくと、入れた順番通りの順番に、データを取り出すことができて、途中のデータだけを取り出すという操作は出来ないことになっています。
またキューにデータを入れることをエンキュー、データを取り出すことをデキューと呼びます。
スタックとは
スタックについても触れておきましょう。スタックはキューとよく比較される対象のデータ構造になります。
机に積み重なった書類を思い出してみてください。最初の書類ほど下になり、新しい書類ほど上に積み重なっていきますよね。
この後入れ先出し、Last In First Out(LIFO)の構造をスタックと呼びます。このスタックにデータを入れていくと、入れた順番とは逆の順番に、データを取り出すことが出来ます。
またこのスタックも途中のデータだけを取り出すという操作は出来ません。このスタックにデータを入れることをプッシュ、データを取り出すことをポップと呼びます。
基本的にはスタックとキューは別の扱いになることが多いのですが、Pythonのqueueモジュールではそれぞれのクラスがまとめられています。
queueモジュールを使用するには
上記で解説したスタックとキューを自分で実装するのは意外と簡単なのですが、このqueueモジュールを使えばメソッドの呼び出しだけで更に簡単に使うことが出来ます。
queueモジュールを使用するには、まずimportしておく必要があります。
import queue
これでエラーが出なければimportは成功です。
もしPython2系を使っているのであれば、
import Queue
のように、大文字で指定してみてください。
Python2系と3系ではこのように大文字表記から小文字表示に変更されているので気をつけましょう。以降この記事ではPython3系について解説をしていきます。
queueモジュールの基本的な使い方
FIFOキュー(キュー)
それでは一般的なキューを使ってみましょう。
こちらのコードをご覧ください。
from queue import Queue q = Queue() abc = ['a','b','c','d','e'] for i in abc: q.put(i) print("Enqueue:"+i) print("--------") while True: if q.empty(): break else: i = q.get() print("Dequeue:"+i)
実行結果
Enqueue:a Enqueue:b Enqueue:c Enqueue:d Enqueue:e -------- Dequeue:a Dequeue:b Dequeue:c Dequeue:d Dequeue:e
まずはqueueモジュールからQueueクラスを選んでimportしています。
importの詳しい解説はこちらの記事をご覧ください。
そしてabcというリストの中にある文字を一つずつエンキューしています。その後にqが空になるまで、qの中身をデキューしています。実行結果に並ぶ順番が同じなので、FIFOが確認出来ましたね。
LIFOキュー(スタック)
次はスタックを使ってみましょう。
こちらのコードをご覧ください。
from queue import LifoQueue q = LifoQueue() abc = ['a','b','c','d','e'] for i in abc: q.put(i) print("Enqueue:"+i) print("--------") while True: if q.empty(): break else: i = q.get() print("Dequeue:"+i)
実行結果
Enqueue:a Enqueue:b Enqueue:c Enqueue:d Enqueue:e -------- Dequeue:e Dequeue:d Dequeue:c Dequeue:b Dequeue:a
まずは先程のように、queueモジュールからLifoQueueクラスを選んでimportしています。そしてabcというリストの中にある文字を一つずつプッシュしています。その後にqが空になるまで、qの中身をポップしています。
実行結果に並ぶ順番がちょうど逆になっているので、LIFOが確認出来ましたね。
ちなみにputメソッドとgetメソッドはパブリックで用意されているので、キューとスタックでは共通で使うことが出来ます。
queueモジュールでマルチスレッドを扱う
ここまでは基本的なqueueモジュールの扱い方について見てきました。ここからは関連性のあるマルチスレッドプログラミングについて、簡単に学んでいきましょう。
PythonはC言語などに比べて比較的速度の遅いプログラミング言語ですが、処理速度を上げたいような重い処理がある場合は、マルチスレッドを扱った並行処理をする必要が出てきます。
基本的なプログラムはシングルスレッドで、上から下に向けて順番に、一つ一つ処理を終わらせていくのが基本です。ただ、マルチスレッドの場合は、ある処理を行いながら、もう一つの処理を同時に行うことで処理速度を上げることが出来ます。
それでは早速コードを見ていきましょう。
import threading, queue import time def printNum(num, q): for i in num: print("Num:",i) time.sleep(5) q.put(i) def addX(q): while True: _num = q.get() _num += 10 print("Num(add10):",_num); time.sleep(10) q.task_done() q = queue.Queue() for n in range(2): thread = threading.Thread(target=addX, args=(q,)) thread.start() num = [1,2,3,4,5,6,7,8,9] printNum(num, q) q.join()
このコードではthreadingとtimeというモジュールをimportしていますがここで必要なのはthreadingモジュールです。このthreadingモジュールでprintNum関数とaddX関数を並行処理させています。
また並行処理をさせるためにキューを使って、それぞれの関数で値を渡して<います。並行処理はプログラミングの中でも共通してやや難しい分野になっているので、ここではこのようなプログラミングの仕方もあるんだといった程度に覚えておきましょう。
まとめ
今回はqueueモジュールについて、キューとスタック<を解説してきました。基本的なデータ構造についてわかって頂けたでしょうか?
queueモジュールでは同じメソッドを違うクラスでも使うことが出来ます。
もしキューやスタックについて忘れてしまったときは、またぜひこの記事をご覧ください!