1. type() function
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.
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 User
searches 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
, metaclass
it uses Model
the one defined in metaclass
it ModelMetaclass
to create a User
class, 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:
-
excludeModel
modifications to the class; -
Find all the attributes of the defined class in the current class (for exampleUser
), 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); -
Save the table name to__table__
, here it is simplified as the table name defaults to the class name.
In the Model
class, you can define various methods of operating the database, such as save()
, delete()
, find()
, update
and 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, INSERT
statements can be constructed.
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]
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.
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.