python ORM理解、元类
元类
参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000
1)type()
结论:动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。正常情况下,我们都用 class Xxx... 来定义类,但是, type() 函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。
教程里的 self 代表的是类的实例。补充一下 self.__class__ 则指向类。
参考链接:https://www.cnblogs.com/jessonluo/p/4717140.html
然后作者举了一个例子,用类创建实例的过程引入用什么来创建类的这个问题,答案是用 type() , type() 函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过 type() 函数创建出 Hello 类
>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'>
参数:使用 type() 来创建一个class,依次传入三个参数:class名称;继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;class的方法名称与函数绑定,这里我们把函数 fn 绑定到方法名 hello 上。
通过 type() 函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用 type() 函数创建出class。
2)元类
元类是用来创建类的,他们之间的关系就是类和实例之间的关系,换句话说,你可以把类看成是metaclass创建出来的“实例”。
然后作者举了一个例子,用metaclass来为自定义的myList增加一个add方法
# metaclass是类的模板,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs):#参数依次是当前准备创建类的对象、类的名称、类继承的父类集合、类的方法集合 attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs)
之后再在自定义类的代码中传入关键字参数mataclass:
class MyList(list, metaclass=ListMetaclass): pass
当我们传入关键字参数 mataclass 时,魔术方法就生效了,这个参数会指示python解释器在创建这个MyList类时,要通过 ListMetaclass.__new__() 来创建,在上面这个new中,我们就可以修改类的定义。比如,加上新的方法,然后,返回修改后的定义。
最后,作者说了一句乍看起来非常让人不明所以但冷静分析一下还是有道理的话,那就是上面举的例子很智障,因为我们明明可以在MyList定义的代码中假如add方法,但是作者也说了,这是是一个例子而已,是为了让我们明白这个元类可以用来定义类的代码,而总是会有需要用到元类的地方,比如ORM
ORM
但是,总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。
1)作用
补充:
为什么所有的类必须只能动态生成呢,因为这个框架是把数据库中的每一行转化为每一个对象,说的再通俗一点,这个框架会用在很多数据库中,而每个数据库有多个表,而如果每个表都自己定义一个类,那么就失去了框架的通用性,就没有必要了。
ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。为什么呢,一个表定义一个类不就行了,或许这个ORM就是解决这个的,只创建一个类,然后根据表名动态的创建不同的类?
2)创建ORM
ORM是一个类
作者给出了创建ORM的一种方法,就是从使用者的角度,思考你想怎么去使用它,然后再根据接口去定义ORM
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()
其中的父类Model、属性类型 StringField 、 IntegerField 是由ORM提供的,剩下的魔术方法比如 save() 全部由 metaclass 自动完成。虽然 metaclass 的编写会比较复杂,但ORM的使用者用起来却异常简单。
首先来定义 Field 类,它负责保存数据库表的字段名和字段类型:
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)
在 Field 的基础上,进一步定义各种类型的 Field ,比如 StringField , IntegerField 等等:
class StringField(Field): def __init__(self, name): super(StringField, self).__init__(name, 'varchar(100)')#调用super时,不用传入self class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint')
补充:
super()
参考链接:https://www.runoob.com/python/python-func-super.html
是用于调用父类或者超类的方法
# super(StringField,self) 首先找到 StringField 的父类(就是类 Field),然后把类 StringField 的对象转换为类 Field 的对象,然后再调用Field的__init__方法
最复杂的:就是编写最复杂的 ModelMetaclass 了:
在 ModelMetaclass 中,一共做了几件事情:
-
排除掉对 Model 类的修改;
-
在当前类(比如 User )中查找定义的类的所有属性,如果找到一个 Field 属性,就把它保存到一个 __mappings__ 的dict中,同时从类属性中删除该 Field 属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性,但是当删除类属性的时候,访问的将会是类的属性);
-
把表名保存到 __table__ 中,这里简化为表名默认为类名。
class ModelMetaclass(type): def __new__(cls, name, bases, attrs): if name=='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): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v for k in mappings.keys(): attrs.pop(k) attrs['__mappings__'] = mappings # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs)
以及基类 Model :
当用户定义一个 class User(Model) 时,Python解释器首先在当前类 User 的定义中查找 metaclass ,如果没有找到,就继续在父类 Model 中查找 metaclass ,找到了,就使用 Model 中定义的 metaclass 的 ModelMetaclass 来创建 User 类,也就是说, metaclass 可以隐式地继承到子类,但子类自己却感觉不到。
可以定义各种操作数据库的方法,比如 save() , delete() , find() , update 等等。
class Model(dict, metaclass=ModelMetaclass): def __init__(self, **kw): super(Model, self).__init__(**kw) def __getattr__(self, key): 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 = [] 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))
别的一些写在这里
参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014323389656575142d0bcfeec434e9639a80d3684a7da000
解释:https://www.jianshu.com/p/ac8a9bb57ec3
说实话,第一遍看完廖老师的ORM我是完全懵逼的,这技术、这思想真是秀啊,可是这技术太高端了、对于我这样的小白来说跨度太大了。可是看不懂能怎么办啊、于是又看别的orm介绍,刚好找到一篇别人写的ORM学习笔记,我觉得很有参考价值,他根据自己的了解把教程中的ORM教程的各个步骤给做了一个解释。配合这个,我终于能看懂一些了。
1)作用
ORM(Object Relationship Mapping) 的全程是对象关系映射,这种思想的要点在于要把后台对数据库的各种操作如增删改查中的一些步骤给抽象出来,把数据表映射成一个类,表中的行给作为一个实例,行中的每个字段作为属性,这样以来以前对数据库插入一行数据的操作就可以简化为
user=User(id="100001",name="Andy",password="*****") user.save() //保存到数据库
这个ORM和三层架构好像有一些相似之处,他们都是将数据与SQL独立开来,让使用时只需要传递数据,调用方法,而不去一遍一遍的拼接sql语句。
弄清楚了:ORM包括字段类Field和Model类的实现