python - type(), metaclass and thin ORM framework

1. type() function

The #type() function can not only return the type of an object, but also create a new type,  
# For example, we can create the Hello class through the type() function without passing the definition of class Hello(object)...:  
  
def fn(self,name='world'): #Define the function first  
    print('Hello, %s.'%name)  
#Start with type()  
hello = type('Hello',(object,),dict(hello=fn))  # 创建Hello class  
#To create a class object, the type() function passes in 3 parameters in turn:  
#class name;  
#Inherited parent class collection, note that Python supports multiple inheritance, if there is only one parent class, don't forget the single-element writing of tuple;  
The method name of #class is bound to the function, here we bind the function fn to the method name hello.

 

The #type() function can not only return the type of an object, but also create a new type,
# For example, we can create the Hello class through the type() function without passing the definition of class Hello(object)...:

def fn(self,name='world'): #Define the function first
    print('Hello, %s.'%name)
#Start with type()
hello = type('Hello',(object,),dict(hello=fn))  # 创建Hello class
#To create a class object, the type() function passes in 3 parameters in turn:
#class name;
#Inherited parent class collection, note that Python supports multiple inheritance, if there is only one parent class, don't forget the single-element writing of tuple;
The method name of #class is bound to the function, here we bind the function fn to the method name 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 Metaclass metaclass allows you to create or modify classes  
class Listmetaclass(type):  
    def __new__(cls, name,bases,attrs):  
        attrs['add'] = lambda self,value:self.append(value) #Add() method  
        return type.__new__(cls,name,bases,attrs)  
class MyList(list,metaclass=Listmetaclass):  
    pass  
The parameters received by the #__new__() method are:  
  
#1. The object of the class currently to be created;  
#2, the name of the class;  
#3. The parent class collection of class inheritance;  
#4, the method collection of the class.  
  
  
#Metaclass is not commonly used in general, but there will always be situations where you need to modify the class definition through metaclass. ORM is a typical example.  
#ORM's full name is "Object Relational Mapping", that is, object-relational mapping, which is to map a row of a relational database into an object.  
# That is, a class corresponds to a table. In this way, it is easier to write code without directly manipulating SQL statements.  
#To write an ORM framework, all classes can only be defined dynamically, because only the user can define the corresponding class according to the structure of the table.  

  

#metaclass Metaclass metaclass allows you to create or modify classes
class Listmetaclass(type):
    def __new__(cls, name,bases,attrs):
        attrs['add'] = lambda self,value:self.append(value) #Add() method
        return type.__new__(cls,name,bases,attrs)
class MyList(list,metaclass=Listmetaclass):
    pass
The parameters received by the #__new__() method are:

#1. The object of the class currently to be created;
#2, the name of the class;
#3. The parent class collection of class inheritance;
#4, the method collection of the class.


#Metaclass is not commonly used in general, but there will always be situations where you need to modify the class definition through metaclass. ORM is a typical example.
#ORM's full name is "Object Relational Mapping", that is, object-relational mapping, which is to map a row of a relational database into an object.
# That is, a class corresponds to a table. In this way, it is easier to write code without directly manipulating SQL statements.
#To write an ORM framework, all classes can only be defined dynamically, because only the user can define the corresponding class according to the structure of the table.

  

if __name__ == '__main__':  
    L = MyList()  
    L.add(2) #Added the add() method  
    L.add(3)  
    print(L)   

if __name__ == '__main__':
    L = MyList()
    L.add(2) #Add() method
    L.add(3)
    print(L)

[2, 3]
 
 

3. Streamlined ORM framework

#Start writing ORM framework  
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')  
#The next step is to write the most complex ModelMetaclass:  
class ModelMetaclass(type):  
    def __new__(cls, name,bases,attrs):  
        if name == 'Model': #Exclude modifications to the Model class;  
            return type.__new__(cls,name,bases,attrs)  
        print('Found Model: %s'%name)  
        mappings = dict()  
        for k,v in attrs.items(): #Find all attributes of the defined class,  
            if isinstance(v,Field): #If a Field property is found,  
                print('Found mapping: %s ==> %s' % (k, v))  
                mappings[k] = v #Save it to a dict of __mappings__  
        for k in mappings.keys():  
            attrs.pop(k) #Delete the Field attribute from the class attribute at the same time, otherwise, it is easy to cause runtime errors (the attribute of the instance will cover the attribute of the same name of the class);  
        attrs['__mappings__'] = mappings # Save the mapping between attributes and columns  
        attrs['__table__'] = name # Assume the table name is the same as the class name and save the table name to __table__  
        return type.__new__(cls,name,bases,attrs)  
  
#Base class Model  
class Model(dict,metaclass=ModelMetaclass):  
    def __init__(self,**kw):  
        super(Model,self).__init__(**kw)  
    def __getattr__(self, item): #Attributes not found, look for them here  
        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))  
  
#Subclass User  
# Define the mapping of class attributes to columns:  
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':#Exclude modifications to the Model class;
            return type.__new__(cls,name,bases,attrs)
        print('Found Model: %s'%name)
        mappings = dict()
        for k,v in attrs.items(): #Find all attributes of the defined class,
            if isinstance(v,Field): #If a Field property is found,
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v #Save it to a _
        For k in mappings.keys(): attrs.pop(k) in the dict of
            _mappings__ #Delete the Field attribute from the class attribute at the same time, otherwise, it is easy to cause runtime errors (the attribute of the instance will cover the attribute of the same name of the class);
        attrs['__mappings__'] = mappings # Save the mapping relationship between attributes and columns
        attrs['__table__'] = name # Assuming the table name is the same as the class name, save the table name to __table__
        return type.__new__(cls,name,bases ,attrs) #Base

class 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)) #Subclass

User
# Define the mapping of class attributes to columns:
class User(Model):
    id = IntegerField('id')
    name = StringField('username ')
    email = StringField('email')
    password = StringField('password')
    age = IntegerField('age')

 

When the user defines one class User(Model), the Python interpreter first Usersearches in the definition of the current class metaclass. If it is not found, it continues to search in the parent class. If it is found Model, metaclassit uses Modelthe one defined in metaclassit ModelMetaclassto create a Userclass, that is, metaclass can hide Inherit to subclasses, but subclasses don't feel it themselves.


In ModelMetaclass, a total of several things have been done:


  1. exclude Modelmodifications to the class;


  2. Find all the attributes of the defined class in the current class (for example User), if you find a Field attribute, save it to a __mappings__dict, and delete the Field attribute from the class attribute at the same time, otherwise, it is easy to cause runtime errors (instance The property of the class will overwrite the property of the same name of the class);


  3. Save the table name to __table__, here it is simplified as the table name defaults to the class name.


In the Modelclass, you can define various methods of operating the database, such as save(), delete(), find(), updateand so on.


We implemented the save()method to save an instance to the database. With table names, attribute-to-field mappings, and collections of attribute values, INSERTstatements can be constructed.

Try writing code:
 
 
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]

 

That is, the class attributes of the User class that are mapped to the columns can be added to the SQL statement, but the class attributes that have not been mapped to the columns: such as height, will be filtered out to realize the field of the database column. Highly customizable
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]

 


Even if the height parameter is passed in during initialization, it will still be filtered out, and the class attribute age that defines the mapping to the column, even if it is not assigned a value, will be defaulted to None and added to the database.
 
Note: As you can see, the save() method has printed out the executable SQL statement and the parameter list. You only need to connect to the database and execute the SQL statement to complete the real function. The following storage operations, as well as the remaining deletion, modification and inspection operations can be resolved by themselves.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325903479&siteId=291194637