[Python] クラスの挙動まとめ #1
- 内容
- クラスの基本的な挙動、新形式クラス、 type() を使ったクラス生成
- 対象
- ある程度プログラミングの知識がある人。
クラスもオブジェクト
Python ではクラスもオブジェクトである。
class Class: """クラスオブジェクトを作成するリテラル Class という変数に "Class という名前のクラス" のオブジェクトを代入している """ pass id(Class) # オブジェクトのアドレス Class.__module__ # オブジェクトの属性にアクセスしている Class.__name__ # クラスオブジェクトのプロパティ __name__ にはクラス名が入っている。ここでは 'Class' Another = Class # 変数だから、代入もできる Another.__name__ # 'Class' obj = Another() isinstance(obj, Class) # True isinstance(obj, Another) # True obj.__class__.__name__ # 'Class' # Another という変数に入っている "Class という名前のクラス" のオブジェクトを呼び出し、インスタンスを作成している。 """ 重要なこと obj = Class() で obj に Class のインスタンスが代入されている。 これは、他の言語で obj = new Class() としているのとは全く違う意味。 Class() は、 Class という変数に入っているオブジェクトを呼び出している (__call__ プロパティの関数をコールしている) だけである。 この場合、 Class に入っているのは "Class という名前のクラス" のオブジェクトである。 このオブジェクトの __call__ を呼び出し、__call__ の中でインスタンスを生成し、それを呼び出し元に返しているに過ぎない。 ようするに、 obj = Class() obj = Class.__call__() は同じ意味である。 (細かいところで違うし、実際上のコードはエラーがでるけど、その部分はまた後で) この理解は後にメタクラスを学習する際に役立つ。 """
なお、 __call__ については Python ライブラリリファレンス - 3.3.4 呼び出し可能オブジェクトをエミュレートする を参照。
新形式クラス
Python には 2 つのクラス形式がある。
- Old-style class 旧形式クラス
- Python 2.2 より前からある普通のクラス。低機能。
- New-style class 新形式クラス
- 2.2 以降から使える。高機能。
基底クラスに object がある場合、そのクラスは新形式クラスとして扱われる。
class Old: """旧形式クラス""" pass class New(object): """新形式クラス""" pass class Derived(New): """これも新形式クラス""" pass type(Old) # <type 'classobj'> dir(Old) # ['__doc__', '__module__'] type(New) # <type 'type'> dir(New) # ['__class__', '__delattr__', '__dict__', ... ] import types isinstance(Old, types.ClassType) # True 旧形式クラスのクラスオブジェクトは types.ClassType のインスタンス isinstance(New, types.ClassType) # False isinstance(Old, type) # False isinstance(New, type) # True 新形式クラスのクラスオブジェクトは type クラスのインスタンス。 Old.__class__ # AttributeError: class Old has no attribute '__class__' # 旧形式クラスでは、クラスオブジェクトの __class__ プロパティは参照できない New.__class__ # <type 'type'> New.__class__.__name__ # 'type'
type() を使った新形式クラスの作成
組み込み関数 type() *1 を使って、新形式クラスのオブジェクトを生成することができる。
Type = type("Type", (), {}) # type(str クラス名, tuple 基底クラス, dict プロパティ) isinstance(Type, type) # True OldDerived = type("OldDerived", (Old,), {}) # TypeError: a new-style class can't have only classic bases # 旧形式クラスだけを基底クラスとして持つようなクラスは type では作成できない。 def someMethod(self): print self, "someMethod called" OldNewDerived = type("OldNewDerived", (Old, New), {"method1": someMethod}) isinstance(OldNewDerived, types.ClassType) # False isinstance(OldNewDerived, type) # True obj = OldNewDerived() isinstance(obj, Old) # True isinstance(obj, New) # True isinstance(obj, OldNewDerived) # True obj.method1() # <__main__.OldNewDerived object at アドレス> someMethod called
よって、以下はほぼ同じ意味である。
class Class(object): pass Class = type("Class", (object,), {})
__new__ だの __metaclass__ だのといったカスタマイズは別に書く。
長くなっちゃうしね。
おまけ
クラスオブジェクトが代入されている変数の名前と、クラスの名前を混同しないように。
Class = type("ClassName", (), {}) # Class という変数に "ClassName という名前のクラス" のオブジェクト (type クラスのインスタンス) を代入 Class.__name__ # 'ClassName' obj = ClassName() # NameError: name 'ClassName' is not defined obj = Class() # OK obj.__class__ # <class '__main__.ClassName'> obj.__class__.__name__ # 'ClassName' ClassName = type("Class", (), {}) Class().__class__.__name__ # 'ClassName' ClassName().__class__.__name__ # 'Class'
おまけ 2
__class__ プロパティは、そのオブジェクトのクラスのオブジェクトが入っている
class Class: pass obj = Class() obj.__class__ is Class # True
*1:正しくはクラスだけど