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

  • 相关阅读:
    SP笔记:交叉实现七行并成一行
    HTML tag 学习
    操作哈希表
    Efficient bipedal robots based on passivedynamic walkers
    Pushing People Around
    ZEROMOMENT PONTTHIRTY FIVE YEARS OF ITS LIFE

    Active Learning for RealTime Motion Controllers
    Accelerometerbased User Interfaces for the Control of a Physically Simulated Character
    Dynamic Response for Motion Capture Animation
  • 原文地址:https://www.cnblogs.com/wilber2013/p/4695836.html
Copyright © 2011-2022 走看看