理解しておくべきことは、メタクラスはそれ自体がクラスではないということです。これは、クラスを返す呼び出し可能オブジェクト(クラス ファクトリ)です。と同じ目的を果たしますtype(__name__, __bases__, __dict__)
。
>>> type('myclass', (), {})
<class '__main__.myclass'>
を定義するとき__metaclass__
、あなたは単にoverriding the default class factory for that specific class or module
です。たとえば、これはメタクラスです。
def setattr_logging_class(name, bases, dict_):
"""I am a metaclass"""
def __setattr__(self, k, v):
print "{} set attribute {} to {}".format(self, k, v)
super(self.__class__, self).__setattr__(k, v)
dict_['__setattr__'] = __setattr__
cls = type(name, bases, dict_)
return cls
class MyClass(object):
__metaclass__ = setattr_logging_class
def __init__(self):
self.a = 1
obj = MyClass()
obj.b = 2
print obj.__dict__
最も重要なことは、 (変更しない限りbases
) 作成されたクラスのメソッド解決にメタクラスが関与しないことです。これが、あなたの がインスタンスVersioned.__setattr__
に表示されない理由です。Test
すべてが同じで新しいクラス ( typeではなく type ) をVersioned
返し、Python ランタイムがブロックから解析されました。Versioned
type
name
bases
dict_
class Test(object):
クラス自体は (__new__
メソッドを介して) 呼び出すことができます。(__new__
はクラス__call__
用であり、インスタンス用です。)
class MyClass(object):
def __new__(cls_self, *args, **kwargs):
print "I am {} called with {} and {}".format(cls_self, args, kwargs)
return None
myobj = MyClass(1,2,3,a=4,b=5,c=6)
print myobj # == None
クラスは呼び出し可能であるため、クラスをメタクラスとして使用できます。実際、type
クラスです。ただし、メソッドでインスタンスを使用することもできます__call__
! これら 2 つの例は似ており、どちらの方法でも実行できないことはありません。(実際、通常、オブジェクトを使用する方が簡単です。)
class MetaClass(type):
def __new__(cls, name, bases, dict_):
print "I am {} called with {}, {}, {}".format(cls, name, bases, dict_)
return type.__new__(cls, name, bases, dict_)
class MetaObject(object):
def __call__(self, name, bases, dict_):
print "I am {} called with {}, {}, {}".format(self, name, bases, dict_)
return type(name, bases, dict_)
class MyClass(object):
__metaclass__ = MetaClass
class MyClass2(object):
__metaclass__ = MetaObject()
Python には、メタクラス以外にも多くのメタプログラミング手法があることに注意してください。特に、Python >= 2.6 では、クラス デコレータのサポートが追加されました。これは、はるかに単純なインターフェイスでメタクラスのユース ケースのほとんどをカバーします。(自分でクラスを作成するのではなく、作成済みのクラス オブジェクトを変更して取得します。)
私が最近書いたこのスタックオーバーフローの回答で、複数のメタプログラミング方法の調査を見ることができます。元の質問は、「組み込みの Python コンテナー タイプをスレッドセーフにするにはどうすればよいですか?」というものでした。