Python dequeのメソッド、使いどころ

さいきんは暇なときにPythonの標準のライブラリのことを調べています。
Pythonは豊富なサードパーティライブラリがあってimportするだけで色々なことができるわけですが、標準のライブラリも知っておいて損はありません。
今回はcollections.dequeについてまとめていきます。

参考ページ
https://docs.python.org/ja/3/library/collections.html

使い方

イテラブルを引数にしてdequeオブジェクトを作れます。

from collections import deque

l = [10, 20, 30]
d = deque(l)
print(d)
>>>
deque([10, 20, 30])


メソッド

一部を紹介します。

要素を末尾に加える

d.append(40)
print(d)
>>>
deque([10, 20, 30, 40])


これはリストと同じですね。

要素を先頭に加える

d.appendleft(0)
print(d)
>>>
deque([0, 10, 20, 30, 40])


リストの場合は先頭に加えるときはinsert(0,要素)とします。
dequeオブジェクトだと直感的ですね。

イテラブルの追加

d.extend([50, 60, 70])
d.extendleft([-10, -20, -30])
print(d)
>>>
deque([-30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70])


要素の取り出し

pop()で最後の要素を返して排除、popleft()で最初の要素を返して排除します。

print(d)
last=d.pop()
print(last,d)
first=d.popleft()
print(first,d)
>>>
deque([-30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70])
70 deque([-30, -20, -10, 0, 10, 20, 30, 40, 50, 60])
-30 deque([-20, -10, 0, 10, 20, 30, 40, 50, 60])

要素をずらす

これがリストにない機能で面白いです。

d.rotate(1)
print(d)
d.rotate(-2)
print(d)
>>>
deque([70, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60])
deque([-20, -10, 0, 10, 20, 30, 40, 50, 60, 70, -30])


ベルトコンベアのように、指定した数だけ位置をずらせます。
正の値を与えれば右に、負の値を与えれば左にずれていくイメージです。

最大長の指定

インスタンス化する際にmaxlenを指定すると固定長になります。
最大長を超える分は自動で排除されます。

iterable = [10, 20, 30]
d = deque(iterable, maxlen=3)
print(d)
d.appendleft(0)
print(d)
>>>
deque([10, 20, 30], maxlen=3)
deque([0, 10, 20], maxlen=3)

使いどころ、使用例

通常のリストを使うより先頭と末尾に追加する場合は性能にすぐれているみたいです。

速度計測

100万回の処理で計測してみました。

import time

d = deque()
start = time.perf_counter()
for i in range(0, 1000000):
    d.append(i)
end = time.perf_counter()
elapsed_time = end - start
print('deque:', elapsed_time)

l = []
start = time.perf_counter()
for i in range(0, 1000000):
    l.append(i)
end = time.perf_counter()
elapsed_time = end - start
print('list', elapsed_time)
>>>
deque: 0.0832438
list 0.09122189999999998


リングメニュープログラム

他にはrotateメソッドが面白そうで、ゲームのリングメニューっぽい感じがありますよね。



こういうやつですね。
それっぽいものを簡易的に作ってみました。

from collections import deque


class RingMenu:

    def __init__(self):
        self.jobs = deque(['見習い戦士', 'ナイト', 'モンク', '黒魔導士', '白魔導士'])

    def cur_right(self):
        self.jobs.rotate(1)

    def cur_left(self):
        self.jobs.rotate(-1)

    def display(self):
        selected = f'**{self.jobs[0]}**'
        print(selected.center(20))
        print()
        print(self.jobs[4].ljust(10) + self.jobs[1].rjust(10))
        print()
        print(self.jobs[3].rjust(8) + '  ' + self.jobs[2].ljust(8))


カーソル操作に合わせて表示を切り替えるクラスです。
動作を確認してみましょう。

menu = RingMenu()
menu.display()
>>>
     **見習い戦士**      


白魔導士             ナイト


    黒魔導士  モンク     


このように見習い戦士が選択されています。
カーソルを右に一つずらします。

menu.cur_right()
menu.display()
>>>
      **白魔導士**      


黒魔導士           見習い戦士


     モンク  ナイト     


リングが右に回りました。
次に左に回してみましょう。

menu.cur_right()
menu.cur_left()
menu.cur_left()
menu.display()
>>>
      **ナイト**       


見習い戦士            モンク


    白魔導士  黒魔導士


麻雀プログラム

dequeと書いてデックと読むらしいのですが、ゲームの山とかデッキの実装と相性が良いんだと思います。
試しに麻雀っぽいプログラムを組んでみました。

class MahjongPai:
    MANZU = [('m', i) for i in range(1, 10)] * 4
    SOZU = [('s', i) for i in range(1, 10)] * 4
    PINZU = [('p', i) for i in range(1, 10)] * 4
    ZIHAI = [('z', i) for i in range(1, 8)] * 4
    ALL_PAI = MANZU + SOZU + PINZU + ZIHAI


class Yama:

    def __init__(self):
        self.yama = deque(MahjongPai().ALL_PAI)
        random.shuffle(self.yama)
        self.wan_pai = None

    def set_wan_pai(self):
        """王牌をセットする"""
        d = deque()
        for i in range(1, 15):
            pai = self.yama.popleft()
            d.append(pai)
        self.wan_pai = d

    def dist_haipai(self):
        """配牌する"""
        haipai = deque()
        for i in range(1, 14):
            pai = self.yama.popleft()
            haipai.append(pai)
        return haipai

    def dist_tsumo(self):
        """ツモ牌を配る"""
        return self.yama.popleft()


class Janshi:

    def __init__(self, name):
        self.name = name
        self.tehai = None

    def get_haipai(self, haipai):
        """配牌を得る"""
        # dequeオブジェクトだとsortが使えないので手配はリスト
        self.tehai = list(haipai)

    def tsumo(self, pai):
        """ツモする"""
        self.tehai.insert(0, pai)

    def dahai(self, index):
        """打牌する"""
        pai = self.tehai[index]
        self.tehai.remove(pai)

    def ri_pai(self):
        """理牌する"""
        self.tehai.sort(key=lambda x: (x[0], x[1]))


これはこのように遊びます。

yama = Yama()
yama.set_wan_pai()
tetsuya = Janshi('asada_tetsuya')
tetsuya.get_haipai(yama.dist_haipai())
tetsuya.ri_pai()
print("**配牌の表示**")
print(tetsuya.tehai)
print("**ツモした後の手配**")
tetsuya.tsumo(yama.dist_tsumo())
print(tetsuya.tehai)
print("**一番左の牌を打牌**")
tetsuya.dahai(1)
print(tetsuya.tehai)


**配牌の表示**
[('m', 5), ('m', 5), ('m', 7), ('p', 1), ('p', 5), ('p', 6), ('p', 7), ('s', 3), ('s', 3), ('s', 6), ('s', 7), ('z', 5), ('z', 7)]
**ツモした後の手配**
[('m', 8), ('m', 5), ('m', 5), ('m', 7), ('p', 1), ('p', 5), ('p', 6), ('p', 7), ('s', 3), ('s', 3), ('s', 6), ('s', 7), ('z', 5), ('z', 7)]
**一番左の牌を打牌**
[('m', 8), ('m', 5), ('m', 7), ('p', 1), ('p', 5), ('p', 6), ('p', 7), ('s', 3), ('s', 3), ('s', 6), ('s', 7), ('z', 5), ('z', 7)]


以上、dequeについての記事でした。

TOPページ