zoukankan      html  css  js  c++  java
  • 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有一套规则:

    1. Python解释器会在当前类中查找"__metaclass__"属性对于的代码,然后创建一个类对象
    2. 如果没有找到"__metaclass__"属性,会继续在父类中寻找"__metaclass__属性",并尝试前面同样的操作
    3. 如果在任何父类中都找不到"__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__"属性,可以修改创建的类。也就是说,元类做了下面的事情:

    1. 拦截类的创建
    2. 根据"__metaclass__"对应的代码修改类
    3. 返回修改之后的类

    元类的__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来创建类对象。

  • 相关阅读:
    HTTP协议:POST请求
    HTTP协议:GET请求
    HTTP协议
    理解server.xml
    用session实现简单的购物
    session:的工作原理
    session:的生命周期
    session:例子代码(一个session为一个会话而服务)
    session概述&作用
    时间复杂度和空间复杂度
  • 原文地址:https://www.cnblogs.com/wilber2013/p/4695836.html
Copyright © 2011-2022 走看看