ラベル 型チェック の投稿を表示しています。 すべての投稿を表示
ラベル 型チェック の投稿を表示しています。 すべての投稿を表示

2015年12月3日

NoneTypeの定義

NoneType の定義に関する注意点について述べます。

None の型は NoneType です。

>>> type(None)
<type 'NoneType'>

上記のように、None が NoneType であるというのは明らかな事実なのですが、NoneType を明示的に使おうとするとエラーになってしまいます。
>>> n = None

# NoneType を直接使う
>>> isinstance(n, NoneType)
Traceback (most recent call last):
  File "<stdin>", line 1, in 
NameError: name 'NoneType' is not defined

理由はよく分かりませんが、NoneType は直接使用できないようになっているようです。None チェックするには is None とすべき、という指針への暗黙の推奨なのかもしれません。
とは言え、NoneType を使いたい場合もあります。そのためには、type(None) と書く必要があります。
# type(None)を使用
>>> isinstance(n, type(None))
True

# type(None)を一旦変数に代入
>>> MyNoneType = type(None)
>>> isinstance(n, MyNoneType)
True



参考までに、Python 2 では typesライブラリの中に、NoneType が定義されていました。
# Python 2
>>> import types

>>> isinstance(None, types.NoneType)
True

2015年11月5日

純粋なintチェック

Python では、bool型は int型のサブクラスとして定義されています。
その結果、、、
>>> b = True
>>> isinstance(b, int)
True  # boolはintの一種!
というやや意外な結果が得られます。


bool ではない純粋な int の型チェックをするには、以下のようにします。
>>> i = 10
>>> isinstance(i, int) and not isinstance(i, bool)
True


上の条件式は関数にしておけば扱いやすくなるかと思います。
>>> def is_int(i):
>>>     return isinstance(i, int) and not isinstance(i, bool)
...
>>> is_int(10)
True
>>> is_int(True)
False

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 以外にも多くの抽象基底クラスが定義されています。他のクラスについては項を改めて紹介します。