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类的实现
python ORM理解、元类的更多相关文章
- 深刻理解Python中的元类metaclass(转)
本文由 伯乐在线 - bigship 翻译 英文出处:stackoverflow 译文:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上很热 ...
- 深刻理解Python中的元类(metaclass)
译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...
- [转] 深刻理解Python中的元类(metaclass)
非常详细的一篇深入讲解Python中metaclass的文章,感谢伯乐在线-bigship翻译及作者,转载收藏. 本文由 伯乐在线 - bigship 翻译.未经许可,禁止转载!英文出处:stacko ...
- [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式
使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...
- 深刻理解Python中的元类(metaclass)【转】
译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...
- 深刻理解Python中的元类(metaclass)以及元类实现单例模式
在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍 ...
- 深入理解Python中的元类(metaclass)
原文 译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍 ...
- 深度理解python中的元类
本文转自:(英文版)https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python (翻译版) http:// ...
- python——深刻理解Python中的元类(metaclass)
译注:这是一篇在Stack overflow上 很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉 ...
- python基础——使用元类
python基础——使用元类 type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的. 比方说我们要定义一个Hello的class,就写一个hello. ...
随机推荐
- lua-C++ userdata使用
lua-C++ userdata使用 所负责的产品使用非常灵活,可设置的参数上千个,而且还支持用户用lua进行流程控制,所以开发中要用到很多lua.C++混合编程.之前对这些也还是一知半解,只会依葫芦 ...
- swift学习笔记 - swift中常用关键字
swift中常用关键字 **用作声明的关键字: ** class.deinit.enum.extension.func.import.init.let.protocol.static.struct.s ...
- [APIO2014]回文串(回文自动机)
题意 给你一个由小写拉丁字母组成的字符串 s.我们定义 s 的一个子串的存在值为这个子串在 s 中出现的次数乘以这个子串的长度. 对于给你的这个字符串 s,求所有回文子串中的最大存在值. |S|< ...
- adb如何连接mumu模拟器并修改Android ID
adb工具下载安装 https://dl.google.com/android/repository/platform-tools-latest-windows.zip 参考:https://blog ...
- Docker之Mysql安装及配置
原文:Docker之Mysql安装及配置 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zhaobw831/article/details/8014 ...
- LaTeX 矩阵
本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/50054363 LaTeX 写矩阵,需要 ...
- ASP.NET-让html代码输出为字符串
HttpUtility.HtmlEncode这个函数可以让s变成没有攻击的代码,可以提升网站的安全性 public string Index(string s = "suepr name i ...
- Objective-C对象与Core Foundation对象
Core Foundation 对象主要使用在用C语言编写的Core Foundation 框架中,并引用计数的对象.与Objective-C对象差别非常少.不管哪种框架生成的对象,一旦生成,便可在两 ...
- cocos2dx 几个精灵依照顺序播放动画解决方法
我先描写叙述一下这个问题: 拿之前做的卡牌游戏来说.假设一方场上有3张牌,那么肯定要以一种顺序来播放攻击动画.我是以从左到右的方式. 我的解决方案是向每张牌都传递一个延时參数,然后在runAction ...
- 【android】解决Viewpager设置高度为wrap_content无效的方法
今天发现设置viewpager高度为wrap_content时并没作用.stackoverflow给出了解决方式,就是自己定义viewpager,重写onMesure()方法: public clas ...