2015年11月4日

collections.abcで型チェック

collections.abc を用いた型チェックの方法を紹介します。

collections.abc にはコンテナのための様々な抽象基底クラスが定義されています(ちなみに、abc は"Abstract Base Classes"の略です)。この抽象基底クラスを使うことで、可読性が高く、さらに汎用性もある型チェックを行うことができます。



以下のようにコンテナを前半と後半に分割する関数 divide() について考えます。

def divide(data):
    # スライスで分割
    half = len(data) // 2
    return (data[:half], data[half:])


ここで引数 data はどのような型であるべきでしょうか?
スライス機能を受け入れる型であれば動いてくれそうです。すぐに思いつくのは list、tuple、str 辺りでしょうか。もう少し調べると、range、bytes、bytearray、memoryview もスライス機能を受け入れるようです。
では、divide() をより安全な関数とすべく、引数 data の型チェックを追加してみましょう。
def divide(data):
    # dataの型チェック
    if not isinstance(data, (list, tuple, str, range, bytes, bytearray, memoryview)):
        raise TypeError

    # スライスで分割
    half = len(data) // 2
    return (data[:half], data[half:])


全くもって醜いソースコードになってしまいました。醜いだけならまだしも、以下の2つは明らかに欠点です。
① 7つの型を列挙したが、まだ漏れがあるかもしれない。
② data が自作コンテナクラスである場合、(たとえスライス機能があっても)TypeError になる可能性がある。

このような時に役に立つのが collections.abc を用いた型チェックです。スライス機能があるかどうかは collections.abc.Sequence を使うことで確認できます。
collections.abc.Sequenceを使うと型チェックは以下のようになります。

def divide(data):
    # dataの型チェック
    from collections.abc import Sequence
    if not isinstance(data, Sequence):
        raise TypeError

    # スライスで分割
    half = len(data) // 2
    return (data[:half], data[half:])

可読性が向上しましたし、欠点①と②がいずれも解決しています。Coolですね!


collections.abc には Sequence 以外にも多くの抽象基底クラスが定義されています。他のクラスについては項を改めて紹介します。

0 件のコメント:

コメントを投稿