Python中的元类
从前面"Python对象"文章中了解到,在Python中一切都是对象,类可以创建实例对象,但是类本身也是对象。
class C(object): pass c = C() print c.__class__ print C.__class__
代码中,通过"__class__"属性来查看对象的类型,对于类C对象本身,它的类型是type。
由于类也是对象,所以就可以在运行时动态的创建类,那么这时候就要用到内建函数type了。
再看type
从前面的文章了解到,可以通过内建函数type来获取对象的类型。
class C(object): pass c = C() print c.__class__ is type(c) print C.__class__ is type(C)
这里,我们就看看内建函数type的另一个强大的功能,动态的创建类。当使用type创建类的时候,有以下形式:
type(类名, 父类的元组(可以为空), 属性的字典)
- 要创建的class的名字
- 父类集合,如果只有一个父类,别忘了tuple的单元素写法
- class的属性字典
看看type创建类的例子:
def printInfo(self): print "%s is %d years old" %(self.name, self.age) S = type("Student", (object, ), {"name": "Wilber", "age": 28, "printStudentInfo": printInfo}) print type(S) s = S() print type(s) s.printStudentInfo()
例子中,通过type动态的创建了一个Studnent类,并且通过这个类可以创建实例:
__metaclass__
函数type实际上是一个元类,元类就是用来创建类的"模板"。我们可以通过类"模板"创建实例对象,同样,也可以使用元类"模板"来创建类对象;也就是说,元类就是类的类。
在创建一个类的时候,可以设置"__metaclass__"属性来指定元类。
"__metaclass__"属性对应的代码就是创建类的代码(这段代码可以是一个函数,也可以是一个类);如果这段代码是类,"__metaclass__"的类名总是以Metaclass结尾,以便清楚地表示这是一个元类。
对于元类的查找,Python有一套规则:
- Python解释器会在当前类中查找"__metaclass__"属性对于的代码,然后创建一个类对象
- 如果没有找到"__metaclass__"属性,会继续在父类中寻找"__metaclass__属性",并尝试前面同样的操作
- 如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建这个类对象
def queueMeta(name, bases, attrs): attrs['InQueue'] = lambda self, value: self.append(value) def deQueue(self): if len(self) > 0: return self.pop(0) attrs['DeQueue'] = deQueue # 直接调用type内建函数 return type(name, bases, attrs) # 元类从`type`类型派生 class QueueMetaclass(type): def __new__(cls, name, bases, attrs): attrs['InQueue'] = lambda self, value: self.append(value) def deQueue(self): if len(self) > 0: return self.pop(0) attrs['DeQueue'] = deQueue # 直接调用type内建函数 # return type(name, bases, attrs) # 通过父类的__new__方法 return type.__new__(cls, name, bases, attrs) class MyQueue(list): # 设置metaclass属性,可以使用一个函数,也可以使用一个类,只要是可以创建类的代码 #__metaclass__ = queueMeta __metaclass__ = QueueMetaclass pass q = MyQueue("hello World") print q q.InQueue("!") print q q.DeQueue() print q
代码中的MyQueue类型继承自list,但是通过设置"__metaclass__"属性,可以修改创建的类。也就是说,元类做了下面的事情:
- 拦截类的创建
- 根据"__metaclass__"对应的代码修改类
- 返回修改之后的类
元类的__init__和__new__
当创建元类的时候,为了定制创建出来的类的特性,一般会实现元类的"__init__"和"__new__"方法。
class MyMetaclass(type): def __new__(meta, name, bases, attrs): print '-----------------------------------' print "Allocating memory for class", name print meta print bases print attrs return super(MyMetaclass, meta).__new__(meta, name, bases, attrs) def __init__(cls, name, bases, attrs): print '-----------------------------------' print "Initializing class", name print cls print bases print attrs super(MyMetaclass, cls).__init__(name, bases, attrs) class MyClass(object): __metaclass__ = MyMetaclass def foo(self, param): pass barattr = 2
通过这个例子演示了使用元类的"__init__"和"__new__"方法:
元类的__call__
"__call__"是另外一个经常在实现元类时被重写的方法,与"__init__"和"__new__"不同的是,当调用"__call__"的时候,类已经被创建出来了,"__call__"是作用在类创建的实例过程。
看下面的代码:
class MyMetaclass(type): def __call__(cls, *args, **kwds): print '__call__ of ', str(cls) print '__call__ *args=', str(args) return type.__call__(cls, *args, **kwds) class MyClass(object): __metaclass__ = MyMetaclass def __init__(self, a, b): print 'MyClass object with a=%s, b=%s' % (a, b) print 'gonna create foo now...' foo = MyClass(1, 2)
代码的输出为:
元类使用举例
前面已经介绍了很多关于元类的知识了,下面看看怎么实际使用元类。
元类在ORM中是比较常用的,因为需要在运行时创建类型,看下面简单的例子:
class Field(object): def __init__(self, fname, ftype): self.fname = fname self.ftype = ftype def __str__(self): return '{%s: (%s, %s)}' % (self.__class__.__name__, self.fname, self.ftype) class StringField(Field): def __init__(self, fname): super(StringField, self).__init__(fname, 'varchar(100)') class IntegerField(Field): def __init__(self, fname): super(IntegerField, self).__init__(fname, 'bigint') class ModelMetaclass(type): def __new__(cls, name, bases, attrs): if name == "Model": return super(ModelMetaclass, cls).__new__(cls, name, bases, attrs) else: mapping = {} print "Create Model for:", name for k, v in attrs.items(): if isinstance(v, Field): print "mapping %s with %s" %(k, v) mapping[k] = v attrs['_table'] = name attrs['_mapping'] = mapping return type.__new__(cls, name, bases, attrs) class Model(dict): __metaclass__ = ModelMetaclass def __init__(self, **kwargs): for key in kwargs.keys(): if key not in self.__class__._mapping.keys(): print "Key '%s' is not defined for %s" %(key, self.__class__.__name__) return super(Model, self).__init__(**kwargs) def save(self): fields = [] params = [] args = [] for k, v in self.__class__._mapping.items(): fields.append(k) params.append("'{0}'".format(self[k])) sql = 'insert into %s (%s) values (%s)' % (self.__class__._table, ','.join(fields), ','.join(params)) print 'SQL: %s' %sql class Student(Model): id = IntegerField('id_c') name = StringField('username_c') email = StringField('email_c') print "-------------------------------------------------" print Student._table print Student._mapping print "-------------------------------------------------" s1 = Student(id = 1, name = "Wilber", email = "wilber@sh.com") s1.save() print "-------------------------------------------------" s2 = Student(id = 1, name = "Wilber", gender = "male")
代码中通过元类创建Student类,并将类的属性与数据表关联起来:
总结
本文介绍了Python中元类的概念,通过元类可以在运行时创建类。
当用户定义一个类class的时候,Python解释器就会在当前类中查找"__metaclass__"属性,如果找到,就通过该属性对应的代码创建类;如果没有找到,就继续以相同的规则查找父类。如果在任何父类中都找不到"__metaclass__",就会用内置的type来创建类对象。