The thing to understand is that a metaclass is not a class per se--it is a callable that returns a class--a class factory. It serves the same purpose as type(__name__, __bases__, __dict__)
.
>>> type('myclass', (), {})
<class '__main__.myclass'>
When you define __metaclass__
, you are merely overriding the default class factory for that specific class or module
. For example, this is a metaclass:
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__
Most importantly, a metaclass does not participate in the method resolution of the created class (unless you change bases
). This is why your Versioned.__setattr__
is not visible to your Test
instances. All Versioned
did was return a new class (of type Versioned
rather than type type
) with the same name
, bases
and dict_
the Python runtime parsed out of your class Test(object):
block.
Classes are themselves callable (via their __new__
method). (__new__
is for classes what __call__
is for instances.)
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
Since classes are callable, you can use a class as a metaclass. In fact, type
is a class. Although, you can just as well use an instance with a __call__
method! These two examples are similar and there isn't anything you may want to do that can't be done in either way. (In fact, using an object is usually more straightforward.)
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()
Note that Python has many other metaprogramming methods besides metaclasses. In particular, Python >= 2.6 added support for class decorators, which covers most of the use cases for metaclasses with a much simpler interface. (Rather than create the class yourself, you get an already-created class object you modify.)
You can see a survey of multiple metaprogramming methods at this stackoverflow answer I recently wrote. The original question was "How do I make a built-in Python container type threadsafe?"