Python 编写ORM时的重难点掌握
前言:
最近才狠下心来 准备做一个自己的博客 原先FuckBlog项目由于后端小伙伴加班而搁置,因此 作为团队PM的我自己也要开始做技术方面了,准备自己先写一个博客看看。
备注- ORM全称:object related mapping 对象关系映射
0x00 为什么需要写一个ORM
首先,我觉得数据库操作不封装是很傻比的。原来我写了一个数据库导入工具,全篇导出都是sql语句 什么增删改查都他妈齐活的在各个py里面跑来跑去。给大家上一张图:
大家体会到这种乱跑的辛酸了么 从那次失败的架构我就说:再不用orm
我他妈就是一傻逼
其次就是不安全,我这里再给大家举一个反例 使我们FuckBlog
项目里面
后端小伙伴采用了一个异常简单的后台模板 我在查看代码的时候 发现了问题 我们看用户验证的代码:
看到了么 传输的用户名密码 居然是直接放进sql语句拼接的。大家知道怎么构造 password来达到我们想要的任意用户名都可以登录的效果了么 有几个月了 我简单说一下思路 首先引号闭合 闭合之后 然后在+ or +构造万能查询语句 只要能查询成功,就到让session进行赋值。
最后呢 就是在web中 因为:
一处异步调用 处处异步调用
我们在查询数据库中肯定不能因为数据量过大而放弃其他的请求 这样效率非常慢所以这方面的控制 以及上文所述的问题 需要我们去封装 而方便我们的调用和请求。因此我们需要自己写一个orm
工具类
去完成异步的增删改查
0x01 ORM
编写的一些难点
我跟着廖雪峰大神的博客 来学习 期间也看了不少人的跑通代码 自己的疑问点期初看的时候也很多当然 自己慢慢一点一点啃 啃几天就会发现豁然开朗。
代码加上测试一共三百多行,非常简洁,当然我估计注释就能占到八九十行,包括别人的也包括自己第一篇学习的困惑和不了解。也算是比较实用吧。 我先贴我第一版学习跑通的代码 然后呢 我会慢慢在后面进行阐述。
NO1: 为何要使用asyncore
和 aiomysql
这两个库
假设我们调用数据库的请求不知一个,比如用户小王访问我们网站,获取你以前的文章,那么东北社会你关哥也在同一时间访问我们网站,而文章比较多,小王的请求需要5秒才能返回结果。采用同步的话,那社会你关哥 就要等着小王请求王城才能进行请求(获得服务器关注)么? 显然不行,因此需要采用异步的收发请求 。而Python 中实现这个的就是asyncore 他封装了HTTP UDP SSL 的异步协议 可以让单线程 也可以异步收发请求。(如果你想自己实现套字节的异步收发返回,可以小小参考一下我的这篇网络脚本编写)
那aiohttp是什么鬼 aiomysql
又是什么叼东西。他们都是基于asyncore
实现的异步http库 异步mysql 库 调用他们就可以实现异步请求在http 和 mysql 上。记住:一处异步 处处异步
NO2:关于日志的记录
引用日志模块logging
没有什么好说的 需要注意的是 在我们编写服务端的时候 良好的日志记录习惯很重要 不要被pycharm
惯坏了 动不动print
大法 debug
大法 另外我想补充的是:logging
这个吊玩儿,在多线程记录的时候会出错,需要改写其中的某些方法,别问我是如何知道的。对了,
level=logging.INFO
这个INFO大写。
NO3:*args **kw是什么东西?
*args
和 **kwargs
主要用于函数定义。 **kw
就是**kwargs
的缩写,可以传递数量不一的变量,他们的核心是前面的星号 一颗星和两颗星 而不是后面的args 你写成*fuckyou 都没事。只不过是约定俗称而已。那么这个屌丝东西怎么用呢?我们看一段示例代码:(我改自gitbook Python进阶)
输出结果:
我们再来看**kwargs
**kwargs
:该参数允许你将不定长度的键值对,
作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargs
。
你肯定回想 啥叫键值对 带名字的参数又是什么鬼 我们来看这个例子:
怎么样现在传的时候是不是有点小明白 就像传递一个字典一样,比如这样:
主要传进去my_info
是一个字典就好了。
一般来讲传递的顺序是 var
*args **kwargs
这里面就顺便提一下,字典的get方法用法是get(key,value) 如果没有key对应的键值 则返回设定value
比如
返回就是:
email ==> [email protected]
[email protected]
Fuck None
name ==> sly
sly
Fuck None
NO3: yield
和 yield from
语法
我们都知道yield
或者说对yield
比较熟悉 因为我们教学的案例常常就是 yield result 当一个 生成器(英文:generator)。但是我们为了弄清楚这一系列的东西 我们需要首先弄清楚迭代器 生成器这些鬼东西。在这方面 除了官方文档 我还查阅了 这个 这个还有这个 当然 IBM的也不错 等等 我就不一一列出了。
生成器的简析:
我们为啥需要生成器?假设我们需要处理草榴的所有用户信息,如果一次性就处理1000万的用户信息列表非常耗费内存,我们的本地电脑内存不够用,那么就需要一个分批处理草榴用户信息生成器的东西,当我们调用一次的时候返回1000用户信息列表即可 ,下一次调用就返回uid在1001-2000的用户就好了。但是你可能会问:函数里面的return咋知道你是下一次调用还是第三次第四次调用 给你返回你想要的值呢?而这就是yield 存在的神奇方法,它能够记住。
而正因为yield 存在才使得我们不需要拥有存储 1000万草榴用户信息列表,就能逐步得到这些信息。内存占用极小。
那么可能会有人问:适用于生成器的对象自身需要可迭代能力么 也就是说我必须要是一个字典列表啥的才行么?
事实上生成器调用的对象自身必须拥有可迭代能力,之所以你认为传递列表或者字典是你把迭代在Python里面的识别狭义化了。
什么是可迭代
?什么东西可以记住当前调用位置?
可迭代是指一种可以在容器
中逐个提取元素的能力。
(容器:将多个元素组合在一起的数据结构,常见的容器有 dict list tuple str 文本流 以及他们这些的变形比如OrderedDict)
迭代器
的内部状态可以记住当前调用位置
Python是如何识别一个对象拥有可迭代能力?
一个可迭代对象必须具备:
__iter__()
(为啥该命名方法前后有__因为这是Python特殊方法,以示区分,在OOP编程的后续会给大家讲到)
而迭代器
呢则必须具备以下特殊方法:2.x是next()
__iter__()
__next__()
我们来看廖雪峰在IBM workplace上给的yield 斐波拉切数列案例
你最终会发现这个yield 让返回的对象变成了 generator 一个生成器。 它让你关注的点更多的在算法实现上,而非存储上面。
那么我们该如何自己去实现这个迭代器呢?
调用和拥有yield 的函数一样,同上。
如果还有疑问或者我的描述有错误的可以在博客评论中提出(mail写好我回复你会收到邮件~)
解释了yield
我们来看一下 yield from
关于yield from
这个Python 3.3 才支持的东西我们看一下:
首先,yield from 是为了解决什么问题而出现的。
看到上文我们发现一个问题,yield 不能脱离代码单独出来用,在一个生成器中调用另外一个生成器想要用,咋办 那么我们就需要yield from
yield from
的的历史可以参考 这个PEP:
总之大意是原本的yield语句只能将CPU控制权还给直接调用者,当你想要将一个generator或者coroutine里带有yield语句的逻辑重构到另一个generator(原文是subgenerator)里的时候,会非常麻烦,因为外面的generator要负责为里面的generator做消息传递;所以某人有个想法是让python把消息传递封装起来,使其对程序猿透明,于是就有了
yield from
。
对于简单的迭代器,yield from iterable
本质上等于for item in iterable: yield item
的缩写版 yield from
允许子生成器直接从调用者接收其发送的信息或者抛出调用时遇到的异常,并且返回给委派生产器一个值
那么在本次学习orm的过程中你就把他理解为异步执行即可 先别管原理了。
如果大家仍旧很感兴趣 可以参考这篇文章 我能力有限 就不画蛇添足了。
NO4. __pool 和这个__init__ 这些东西是啥?
这个就简单说一下 前面一条杠 就是非公开变量 两条杠私有变量 一条杠能调用 两条杠就不允许外部调用。不像Java 那样有什么private 什么的 别再和我说不能阻止外部调用很不安全 私有变量 不可以 You are Adult.
__str__ __init__ __name__ 这些都是特殊方法(或者魔术方法) 内置的 详细干嘛自己去Python官网看。
NO5. @asyncore.coroutine
是什么鬼?
它能够将一个生成器generator标记为coroutine类型,然后扔进Eventloop 异步执行。
这里我们需要回去复习廖雪峰大神关于asyncore的讲解。(这告诉我们 翘课早晚是要还的 不管你是网课还是自学)
运行这段代码 首先你会看到三个 hello world 然后过了三秒 看到三个hello again这说明 主进程并未在sleep停留 而是直接将任务继续引入Eventloop中进行执行 这相比 那种单步 一直让社会你关哥等的那种先进了一些。
0x03 撰写Python orm 的一些个人解释
NO1:设计Field类意义在哪里?
将数据库的类型与Python进行对应。比如说我们需要对数据库建表或者增删改查 数据库的字段不仅有不同类型 还有是否为主键的设置,这需要我们定一个class 类来进行定义。
这个__str__
你可以理解为这个类的注释 说明。在廖雪峰的定制类一篇有详细说明
print打印出来的信息就是来源于这个类。如果是调试显示的话是__repr__
这里提下:repr()
方法 可以强制返回类似于字符串的东西 str未必
这里有牛人的解释。
其次是后续的继承:
这个super()
方法用于多态继承的,更新变量
使用。
理解Python 中的元类(英文:metalclass
)
我们都知道我们创建的实例一开始都继承object 这个基类 那么当我们想创建类的时候我们需要继承怎么办? 于是就有了基类。用 Tim Peters 的话来说就是我们一般不用这个,只有在写一些orm 或者高级封装的时候才会用到这个黑魔法。
Well, usually you don’t:
Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).
Python Guru Tim Peters
现在我们按照廖雪峰大神的例子场景还原:我们需要一个自定义的list
这个list
能干嘛呢
多一个fuck
方法
调用一次 加一个‘fuck’在list
末尾。是不是觉得很爽?
看看我改写的代码:
如何需要深入了解比如继承父类集合或者cls 是如何由Python 解释器自动提供 我觉得额 最好赶快去看源代码谢谢 ~
对了 顺便提一下 @classmethod 作为一个装饰器 为metalclass 提供方便。
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
如果你觉得有更好的在这方面的解释文章可以分享,可以推荐给我哈~
0x04 后记
忙完PornDetective 这三天就一直忙于这个ORM的学习,在学习过程中很有趣 也很有意思。自己的能力也有所提高。希望这篇文章对大家能够有所帮助。
转载请注明:灯塔水母 » Python 编写ORM时的重难点掌握