[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:正しくはクラスだけど