Дело в том, что метакласс не является классом как таковой - это вызываемый, который возвращает класс - класс , Он служит той же цели, что и type (__ name__, __bases__, __dict __)
.
>>> type('myclass', (), {})
Когда вы определяете __ 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.
Классы сами могут быть вызваны (через их __ 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 __
! . Эти два примера похожи, и нет ничего, что вы, возможно, захотите сделать, что не может быть сделано в любом случае. (Фактически, использование объекта обычно более простое.)
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.)
Вы можете просмотреть обзор нескольких методов метапрограммирования в этот ответ stackoverflow, который я недавно написал . Первоначальный вопрос: «Как создать встроенный контейнер типа Python threadafe?»