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 件のコメント:
コメントを投稿