要理解的是,元类本身并不是一个类——它是一个返回类的可调用对象——一个类工厂。它的目的与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
所做的只是返回一个具有相同Versioned
的新类(类型Versioned
而不是类型type
)name
,bases
并且Python 运行时从您的块中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__
方法一起使用! 这两个示例是相似的,您可能想做的任何事情都不能以任何一种方式完成。(事实上,使用对象通常更直接。)
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 添加了对类装饰器的支持,它涵盖了元类的大多数用例,具有更简单的接口。(而不是自己创建类,你会得到一个你修改的已经创建的类对象。)
您可以在我最近写的这个 stackoverflow 答案中看到对多种元编程方法的调查。最初的问题是“如何使内置的 Python 容器类型线程安全?”