Python | リストの拡張クラスを定義する

Python のコードを書いていると、リストのような振る舞いをするクラスを自作したくなる場面があるかと思います。
本記事では、リストライクなクラスを自作するサンプルを紹介します。
ただし、フレームワーク開発などの特殊な場面以外では推奨はしません

目次

手法1:MutableSequence を継承する

ひとつめの定義方法として、collections.abcMutableSequence を継承するやり方があります。
これは、変更可能なリストを表現するための抽象クラスです。

自作クラスの定義サンプル

ソートが自動で行われるリストクラスの定義例です。
なお、リストが更新されるたびに力任せにソートしているだけであり、パフォーマンス面で実用性はありません。

Python
from collections.abc import MutableSequence


class AutoSortedList(MutableSequence):
    def __init__(self, iterable):
        self.data = [*iterable]
        self.data.sort()

    def __getitem__(self, key):
        return self.data[key]

    def __setitem__(self, key, value):
        self.data[key] = value
        self.data.sort()

    def __delitem__(self, key):
        del self.data[key]

    def __len__(self):
        return len(self.data)

    def insert(self, key, value):
        self.data.insert(key, value)
        self.data.sort()

    def __str__(self):
        return str(self.data)

MutableSequrnce クラスには、以下の5つが抽象メソッドとして定義されています。

  • __getitem__
  • __setitem__
  • __delitem__
  • __len__
  • insert

これらを自前で定義する必要がありますが、これさえ実装すればその他のリスト関連メソッドが自動で機能するようになります。

print 出力を見やすくするために、上記に加え__str__ も追加で実装しています。

実行サンプル

AutoSortedList を利用するサンプルです。
初期化や更新のたびに自動でソートされます。(ただし力技で)
また、append や for ループなども利用できるようになっています。

Python
a = AutoSortedList([10, 5, 1])
print(a)
# [1, 5, 10]

a.append(2)
print(a)
# [1, 2, 5, 10]

for v in a:
    print(v)
# 1
# 2
# 5
# 10

手法2:list を継承する

もうひとつ、list を直接継承するやり方があります。

list を継承する場合は、継承するだけで list と同様の振る舞いをさせることができます。
必要なカスタマイズのみ定義を追加する形となります。

自作クラスの定義サンプル

list を継承する場合のサンプルコードです。
あまり意味ないですが、print メソッドを追加するカスタマイズのみ行っています。

Python
class MyList(list):
    def print(self):
        print(self)

なお、list を継承する場合は self がリストそのものになります。
print(self) とすると、リストに対する標準出力が行われます。
同様に、例えば self.sort() と記述すると、リストのソートが行われます。

実行サンプル

実行サンプルです。
リストそのものの振る舞いは引き継ぎつつ、print メソッドが動作します。

Python
a = MyList([10, 5, 1])
a.print()
# [10, 5, 1]

a.append(2)
a.print()
# [10, 5, 1, 2]

for v in a:
    print(v)
# 10
# 5
# 1
# 2

自作リスト定義パターンの使い分け

リストクラスの自作方法を2パターン紹介しました。
おおよその使い分けは次のようになると思います。

  • リストの内部構造や基本動作のカスタマイズをしたい場合 ⇒ MutableSequence を継承する
  • リストの基本動作を踏襲しつつ機能追加したい場合 ⇒ list を継承する

なお、本記事で紹介した他に UserList を継承するやり方もありますが、本質的な効果は list を継承する方式と同様です。
公式ページにも「list から直接的にサブクラス化できる能力に部分的に取って代わられました」と記載されている通り、あえて UserList を利用する意味は小さいかと思います。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次