python 元类的使用(orm框架搭建)

来源:互联网 发布:nba季后赛各项数据统计 编辑:程序博客网 时间:2024/06/05 10:28

元类是python面向对象编程里面很难理解的一个东西,所谓元类的就是类的类,它用来创建类,在python里面,类也是一个对象!!!.元类的作用就是动态的创建类.

在python里面type就是元类,我们所有的class定义类的语句都会被python解释为使用用type来创建一个类.

废话少说,首先看一下怎么用元类来解决一个动态创建类的需求,要求我们对于某一类型的类的满足某个条件的属性名字都变成大写!比如:

class test: a = 1 b = 2 c = 3

然后test里面的属性是A,B,C。当然这个需求比较奇怪,我们完全可以手动的把类属性定义为大写,但是如果对于类的动态创建要求比较高的话,自己定义类就会很麻烦.这个以后再说.

代码如下:

class TestMetaclass(type):    def __new__(cls,name,bases,attrs):        print(attrs)        attr_names = [ (attr_name.upper(),value) for attr_name,value in attrs.items() if not attr_name.startswith('__')]        print(attr_names)        return super().__new__(cls,name,bases,attrs)class Test(dict,metaclass = TestMetaclass):    f1 = 1    f2 = 2    sS = 3

这样得到的Test就是所有不以_开头的属性都是大写.而且除了Test,任何从TestMetaclass继承的类都是有这样的效果.

=====================我是分隔线========================

下面给出一种元类真正用的比较多的需求,构建ORM,ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

[0]首先需要构建顶层的接口.我们希望这个框架能够给我们提供这样的接口.

class User(Model):    # 定义类的属性到列的映射:    id = IntegerField('id')    name = StringField('username')    email = StringField('email')    password = StringField('password')# 创建一个实例:u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')# 保存到数据库:u.save()

这样很方便,因为我可以随意创建其他的user1,user2等类,只需要调用相应的接口就可以执行相应的sql操作.但是这里就对orm框架有一个要求,我们的数据库的结构是不定的,可能另外的一个user1需要10个属性,user2需要8个属性,而且每个属性还不一样,这样你就必须让你的类在创建的时候执行一些你规定的操作,比如将所有的属性都收集起来,然后执行sql操作的时候在使用.简单的说就是抽象这些属性,让它们在orm框架中可以动态的创建和操作,而不是依靠属性的名字.

所以现在先构建Field类,它用来描述各个属性的名字和属性类型,比如id属性的名字就叫’id’,属性的类型就是’bigint’(我们自己定义的).

class Field(object):    def __init__(self, name, column_type):        self.name = name        self.column_type = column_type    def __str__(self):        return '<%s:%s>' % (self.__class__.__name__, self.name)class StringField(Field):    def __init__(self, name):        super(StringField, self).__init__(name, 'varchar(100)')class IntegerField(Field):    def __init__(self, name):        super(IntegerField, self).__init__(name, 'bigint')

现在需要来编写orm框架的核心内容:元类的继承体系.

class ModelMetaclass(type):    def __new__(cls,name,bases,attrs):        if(name == 'Model'):#如果是Model类的话,就不需要做任何修改            return super().__new__(cls,name,bases,attrs)        mappings = dict()#创建一个映射字典,用来存储当前创建的类的属性        for k,v in attrs.items():#遍历所有属性            if(isinstance(v,Field)):#注意k都是str,v才可能是Filed类型.用户定义的用来描述表的属性,需要修改.其他的属性不要乱动                print('Found mapping: %s ==> %s' % (k,v ))                mappings[k] = v#把这个属性添加到mappings里面,同时删除attrs里面的属性,防止实例属性和类属性冲突        for k in mappings.keys():            attrs.pop(k)        attrs['__mappings__'] = mappings#为attrs添加mappings这个属性,就是把所有自定义的属性都收集到attrs[__mapppings__]        attrs['__table__'] = name#添加表的名字,这里偷个懒,表的名字就是类的名字        return super().__new__(cls,name,bases,attrs)class Model(dict,metaclass = ModelMetaclass):    def __init__(self, **kw):        super(Model, self).__init__(**kw)    def __getattr__(self, key):#目的是让dict的keyerror变成attributeerror,因为使用getattr时处理的是AttributeError        try:            return self[key]        except KeyError:            raise AttributeError(r"'Model' object has no attribute '%s'" % key)    def __setattr__(self, key, value):        self[key] = value    def save(self):        fields = []#fields用来存储属性的名字        params = []#params用来        args = []#args用来        for k,v in self.__mappings__.items():            fields.append(v.name)            params.append('?')#添加占位符            args.append(getattr(self,k,None))#将实例属性的值添加到args里面        sql = 'inser into %s (%s) value (%s)'%(self.__table__,','.join(fields),','.join(params))        print('SQL %s'%sql)        print('ARGS %s'%str(args))

这样就完成了一个orm框架的初步结构

0 0
原创粉丝点击