実は前回は、メタクラス(カスタムメタクラス)で一番力を発揮する__new__メソッドの話と、__call__メソッドの話をわざと省いた。
いや、えらそうなコトを言っておきながら、自分でも少し混乱していたので、こっそりと色々試していたのだ。
で、確認終了したので、ここにまとめる。
(2007.3.2修正しました)
class Class: # 2.xならClass(object)
def __new__(cls, *args):
ins = object.__new__(cls, *args)
return ins
def __init__(self, *args):
...
戻り値を設定しないと、インスタンスの代わりにNoneが戻ってしまうので、要注意。
class MetaClass(type):
def __new__(cls, name, bases, dict):
<処理内容>
return type.__new__(cls, name, bases, dict)
2.MetaClass.__call__:カスタムメタクラスのインスタンスであるクラスが、インスタンスを作る際に実行される。クラスオブジェクトがインスタンスメソッドを作成する際は、ご存知の通りクラスオブジェクトを関数のように呼び出すので、この動作を定義しているだけである。このときのクラスオブジェクトはメタクラスのインスタンスなので、クラスでインスタンスの関数的呼び出しを定義してるときと全く同じだと思えばよい。ここでも、通常の処理を行うなら、以下のように行う。
class MetaClass(type):
def __call__(cls, *args):
ins = object.__new__(cls)
cls.__init__(ins, *args)
return ins
MetaClass.__call__は、自動的にクラスの__init__は呼ばないので、__init__を利用するなら、それを明示的に返す必要がある。class CLASS(metaclass = MetaClass):動作を確認するために、以下のような実験をしてみた。
>>> class MetaClass(type):
... def __new__(cls, name, bases, dict):
... print('metaclass new')
... return type.__new__(cls, name, bases, dict)
... def __call__(cls, *args):
... print('metaclass call')
... return type.__call__(cls, *args)
...
>>> class CLASS(metaclass = MetaClass):
... def __new__(self, *args):
... print('CLASS new')
... return self
...
metaclass new
>>> c = CLASS()
metaclass call
CLASS new
>>> c
<class '__main__.CLASS'>
>>>
メタクラスで定義されたクラスオブジェクトの__new__は、クラスオブジェクトが定義された直後に実行されるているのが、メッセージで確認できる。
# metatest.py
class Singleton(type):
def __new__(cls, name, bases, dict):
dict['instance'] = None
return type.__new__(cls, name, bases, dict)
def __call__(cls, *args):
if cls.instance == None:
cls.instance = type.__call__(cls, *args)
return cls.instance
class X(metaclass = Singleton):
def __init__(self, *args):
self.lst = args
class Y(metaclass = Singleton):
def __new__(self, *args):
self.lst = args
return self
class Xx(X):
pass
>>> from metatest import *
>>> x = X(1,2,3)
>>> x2 = X(4,5,6)
>>> x.lst
(1, 2, 3)
>>> x2.lst
(1, 2, 3)
>>> x2.a = 666
>>> x.a
666
>>> y = Y(5,5,5)
>>> y.lst
(5, 5, 5)
>>> xx = Xx()
>>> xx.lst
()
>>> x.lst
(1, 2, 3)
>>>
同じクラスから作ったインスタンスは同じオブジェクトになっていることが、ご理解いただけただろうか。
>>> class M(type):
... def __call__(cls, *args):
... print('「クラス生成は失敗しました」')
... return
...
>>> class X(metaclass = M):
... pass
...
>>> x = X()
「クラス生成は失敗しました」
>>> x
>>>
あるメタクラスからクラスを作ると、必ず失敗するメタクラス。