Diary?

2009-06-10
Wed

(15:23)

Python であるオブジェクトが特定の型かどうかチェックするには isinstance 関数を使い、その時に「これは関数型か」「これはクラス型か」などのチェックを行うには types モジュールを使う。ただし、その時はちょっと注意が必要なので以下覚書。

まず Python は 2.2 だか 2.3 だったか忘れたが object 型が導入され、クラスの形式に二通りの表現方法が混在している(これは Python 3.0 では新形式に統一)。

# 旧式のクラス
class Foo():
    pass
    
# 新しい書き方はこう(3.0 からはデフォルトで object を継承)
class Bar(object):
    pass

両者の違いの詳細に付いてはドキュメント読めなんだが、その一つとして isinstance したときの挙動の違いがある。

>>> isinstance(Foo, types.ClassType)
True
>>> isinstance(Foo, types.TypeType)
False
>>> isinstance(Bar, types.ClassType)
False
>>> isinstance(Bar, types.TypeType)
True

一体なんでこんな事になってるのかと言うと、昔の Python ではクラスと型が未整理で、統一されているとは言い難かったということがある。例えば 1 という整数リテラルの型を調べてみると、それは int 型(types.IntType)となり、さらに int の型を調べてみると type 型(type.TypeType)となる。つまり、

  • 整数の 1 は int のインスタンス
  • int は type のインスタンス

という階層ができている。ところが旧式のクラスについてはこの階層から外れており、先の Foo クラスのインスタンス foo を作った場合、その型を type で調べた時の戻り値は instance となり、そこから型情報を得ることができない。型情報は __class__ 属性から取得できるが、これはちょっと不統一でわかりにくい。さっきと同じように箇条書きにすると、

  • foo は Foo のインスタンスだが、 type で調べると instance 型となる
  • Foo は type のインスタンスではないが types.ClassType のインスタンスで、それにもかかわらず types.ClassType は type のインスタンス(!!!!!!!)

という割と終わってる混沌状態であり、 2.x 系統の Python でとあるオブジェクトが組み込み型、ユーザ定義クラスを含めた型オブジェクトかどうか調べるには、旧式のクラスも考えた上で以下のように書く必要がある。

isinstance(o, types.ClassType) or isinstance(o, types.TypeType)

こういうのを見てるとさっさと Python 3.0 に移行したくなるけど、でもしばらくは 2.x 系統との共存が続くだろうからな。

Creative Commons
この怪文書はクリエイティブ・コモンズ・ライセンスの元でライセンスされています。引用した文章など Kuwata Chikara に著作権のないものについては、それらの著作権保持者に帰属します。