何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

『Effective Python』Item 28: カスタムコンテナ型はcollections.abcから継承しよう

『Effective Python』の続き。足りないものを補うために。 www.effectivepython.com

自作のコンテナを作る際にはlistとかから直接継承するのではなく、collections.abcから継承して作りましょうという話である。

次の二分木にリストのような機能をつけたいとする。

class BinaryNode(object):
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

リストみたいにインデックスでアクセスしてみたくなる。

class IndexableNode(BinaryNode):
    def _search(self, count, index):
        found = None
        if self.left:
            found, count = self.left._search(count, index)
        if not found and count == index:
            found = self
        else:
            count += 1
        if not found and self.right:
            found, count = self.right._search(count, index)
        return found, count
        # Returns (found, count)

    def __getitem__(self, index):
        found, _ = self._search(0, index)
        if not found:
            raise IndexError('Index out of range')
        return found.value

リストみたいだからlenもいけるはず。

len(tree)

>>>
TypeError: object of type 'IndexableNode' has no len()

残念ながら__len__を実装しなければならないのである。

class SequenceNode(IndexableNode):
    def __len__(self):
        _, count = self._search(0, None)
        return count      

リストみたいに動くならば、indexcountも…となると「何を実装すればよいのか」がわからなくなる。 また、リストよりも複雑な集合や辞書の場合は?となるともっとたいへんである。 そこでcollections.abcで用意されている抽象基底クラスを使えばチェックしてくれるので便利ですよ、という話。

だが、ちょっと落ちていることもある気がしてならない。 『Python文法詳解』の「7.7 抽象基底クラス(P.274)」には次のように書かれている。

しかし、このような、マッピング型の引数には必ずdictの派生型でなければならない、という制限は、Pythonの柔軟性を損なうことになります。 Pythonでは、型に依存せず、ダック・タイピングによるプログラミングで、シンプルで拡張性に富んだ開発を行ってきました。 このようなケースでも、もっと柔軟な対応を行えるようなメカニズムが必要です。

そこで、Python2.6/3.0 では 抽象基底クラス(Abstruct Base Class:ABC) を導入し、より柔軟にクラス階層を構築し、拡張性のある型チェックを行えるようになりました

つまり、実装チェッカとなって便利ですよ、というだけでなく、型チェッカとしてのcollections.abcの重要性も認識するべきと思われる。

これでChapter 3が終わった。実は2015年8月3日からずっと夏休みでChapter 3を訳出&感想エントリ作成が目標であった。 無事終わってよかった…。