python——type()、metaclass元类和精简ORM框架

1、type()函数

#type()函数既可以返回一个对象的类型,又可以创建出新的类型,  
# 比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:  
  
def fn(self,name='world'):  #先定义函数  
    print('Hello, %s.'%name)  
#开始用type()  
hello = type('Hello',(object,),dict(hello=fn))  # 创建Hello class  
#要创建一个class对象,type()函数依次传入3个参数:  
#class的名称;  
#继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;  
#class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。 

 

#type()函数既可以返回一个对象的类型,又可以创建出新的类型,
# 比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:

def fn(self,name='world'):  #先定义函数
    print('Hello, %s.'%name)
#开始用type()
hello = type('Hello',(object,),dict(hello=fn))  # 创建Hello class
#要创建一个class对象,type()函数依次传入3个参数:
#class的名称;
#继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
#class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

  

if __name__ == '__main__':  
    h = hello()  
    h.hello()  
    print(type(hello))  
    print(type(h))  

if __name__ == '__main__':
    h = hello()
    h.hello()
    print(type(hello))
    print(type(h))
Hello, world.  
<class 'type'>  
<class '__main__.Hello'>  

Hello, world.
<class 'type'>
<class '__main__.Hello'>

2、metaclass元类

 
#metaclass   元类   metaclass允许你创建类或者修改类  
class Listmetaclass(type):  
    def __new__(cls, name,bases,attrs):  
        attrs['add'] = lambda self,value:self.append(value) #增加了add()方法  
        return type.__new__(cls,name,bases,attrs)  
class MyList(list,metaclass=Listmetaclass):  
    pass  
#__new__()方法接收到的参数依次是:  
  
#1、当前准备创建的类的对象;  
#2、类的名字;  
#3、类继承的父类集合;  
#4、类的方法集合。  
  
  
#元类一般情况不常用,但总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。  
#ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,  
# 也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。  
#要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。  

  

#metaclass   元类   metaclass允许你创建类或者修改类
class Listmetaclass(type):
    def __new__(cls, name,bases,attrs):
        attrs['add'] = lambda self,value:self.append(value) #增加了add()方法
        return type.__new__(cls,name,bases,attrs)
class MyList(list,metaclass=Listmetaclass):
    pass
#__new__()方法接收到的参数依次是:

#1、当前准备创建的类的对象;
#2、类的名字;
#3、类继承的父类集合;
#4、类的方法集合。


#元类一般情况不常用,但总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。
#ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,
# 也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。
#要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

  

if __name__ == '__main__':  
    L = MyList()  
    L.add(2)  #增加了add()方法  
    L.add(3)  
    print(L)  

if __name__ == '__main__':
    L = MyList()
    L.add(2)  #增加了add()方法
    L.add(3)
    print(L)

[2, 3]
 
 

3、精简ORM框架

#开始编写ORM框架  
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')  
#下一步,就是编写最复杂的ModelMetaclass了:  
class ModelMetaclass(type):  
    def __new__(cls, name,bases,attrs):  
        if name == 'Model':  #排除掉对Model类的修改;  
            return type.__new__(cls,name,bases,attrs)  
        print('Found Model: %s'%name)  
        mappings = dict()  
        for k,v in attrs.items(): #查找定义的类的所有属性,  
            if isinstance(v,Field): #如果找到一个Field属性,  
                print('Found mapping: %s ==> %s' % (k, v))  
                mappings[k] = v  #就把它保存到一个__mappings__的dict中  
        for k in mappings.keys():  
            attrs.pop(k)  #同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);  
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系  
        attrs['__table__'] = name  # 假设表名和类名一致  把表名保存到__table__中  
        return type.__new__(cls,name,bases,attrs)  
  
#基类Model  
class Model(dict,metaclass=ModelMetaclass):  
    def __init__(self,**kw):  
        super(Model,self).__init__(**kw)  
    def __getattr__(self, item):  #没有找到的属性,就在这里找  
        try:  
            return self[item]  
        except KeyError:  
            raise AttributeError(r"'Model' object has no attrs :'%s'"%item)  
  
    def __setattr__(self, key, value):  
        self[key] = value  
  
    def save(self):  
        fields = []  
        params = []  
        args = []  
        for k,v in self.__mappings__.items():  
            fields.append(v.name)  
            params.append('?')  
            args.append(getattr(self,k,None))  
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))  
        print('SQL: %s' % sql)  
        print('ARGS: %s' % str(args))  
  
#子类User  
# 定义类的属性到列的映射:  
class User(Model):  
    id = IntegerField('id')  
    name = StringField('username')  
    email = StringField('email')  
    password = StringField('password')  
    age = IntegerField('age')  

#开始编写ORM框架
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')
#下一步,就是编写最复杂的ModelMetaclass了:
class ModelMetaclass(type):
    def __new__(cls, name,bases,attrs):
        if name == 'Model':  #排除掉对Model类的修改;
            return type.__new__(cls,name,bases,attrs)
        print('Found Model: %s'%name)
        mappings = dict()
        for k,v in attrs.items(): #查找定义的类的所有属性,
            if isinstance(v,Field): #如果找到一个Field属性,
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v  #就把它保存到一个__mappings__的dict中
        for k in mappings.keys():
            attrs.pop(k)  #同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致  把表名保存到__table__中
        return type.__new__(cls,name,bases,attrs)

#基类Model
class Model(dict,metaclass=ModelMetaclass):
    def __init__(self,**kw):
        super(Model,self).__init__(**kw)
    def __getattr__(self, item):  #没有找到的属性,就在这里找
        try:
            return self[item]
        except KeyError:
            raise AttributeError(r"'Model' object has no attrs :'%s'"%item)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k,v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self,k,None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

#子类User
# 定义类的属性到列的映射:
class User(Model):
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')
    age = IntegerField('age')

 

当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到,就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclassModelMetaclass来创建User类,也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。


ModelMetaclass中,一共做了几件事情:


  1. 排除掉对Model类的修改;


  2. 在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);


  3. 把表名保存到__table__中,这里简化为表名默认为类名。


Model类中,就可以定义各种操作数据库的方法,比如save()delete()find()update等等。


我们实现了save()方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句。

编写代码试试:
 
 
if __name__ == '__main__':  
    u = User(id = 12345,name = 'john',email = '[email protected]',password = '666666')  
    u.age = 12  
    u.save()  

if __name__ == '__main__':
    u = User(id = 12345,name = 'john',email = '[email protected]',password = '666666')
    u.age = 12
    u.save()
 

 

Found Model: User  
Found mapping: id ==> <IntegerField:id>  
Found mapping: name ==> <StringField:username>  
Found mapping: email ==> <StringField:email>  
Found mapping: password ==> <StringField:password>  
Found mapping: age ==> <IntegerField:age>  
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)  
ARGS: [12345, 'john', '[email protected]', '666666', 12]  

Found Model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: age ==> <IntegerField:age>
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)
ARGS: [12345, 'john', '[email protected]', '666666', 12]

 

即User类中做好了到列的映射的类属性,都能添加到sql语句中,而未做好了到列的映射的类属性:如height,则会过滤掉,实现数据库列的字段的高度可定制
if __name__ == '__main__':  
    u = User(id = 12345,name = 'john',email = '[email protected]',password = '666666',height = 180)  
    # u.age = 12  
    u.save()  

if __name__ == '__main__':
    u = User(id = 12345,name = 'john',email = '[email protected]',password = '666666',height = 180)
    # u.age = 12
    u.save()
Found Model: User  
Found mapping: id ==> <IntegerField:id>  
Found mapping: name ==> <StringField:username>  
Found mapping: email ==> <StringField:email>  
Found mapping: password ==> <StringField:password>  
Found mapping: age ==> <IntegerField:age>  
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)  
ARGS: [12345, 'john', '[email protected]', '666666', None]  


Found Model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: age ==> <IntegerField:age>
SQL: insert into User (id,username,email,password,age) values (?,?,?,?,?)
ARGS: [12345, 'john', '[email protected]', '666666', None]

即使在初始化的时候传进了height参数,依然会被过滤掉,而定义好了到列的映射的类属性age,即使没有赋值,也会被默认为None,并添加到数据库中。
 
注:可以看到, save()方法已经打印出了可执行的SQL语句,以及参数列表,只需要真正连接到数据库,执行该SQL语句,就可以完成真正的功能。下面入库操作,还有剩下的删改查操作可自行解决。

猜你喜欢

转载自www.cnblogs.com/MY0213/p/9011739.html