『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
リストみたいに動くならば、index
やcount
も…となると「何を実装すればよいのか」がわからなくなる。
また、リストよりも複雑な集合や辞書の場合は?となるともっとたいへんである。
そこで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を訳出&感想エントリ作成が目標であった。 無事終わってよかった…。