一、扯淡
从上一篇 Requests的用法 到现在已经过去半个多月了,这半个多月我一直在抽时间看 Requests 源码,所以今天将这半个月学习到的零零碎碎知识分享出来,以供大家学习和批评。
- 优秀的开源代码
优秀的开源代码就是有诸多的开发者在持续维护更新且开发者极其活跃。不像某些开源代码热于一时,后续再没见过有提交记录,也许就像大众那么讲的:
“他们就是为了拿到很高的Star,然后冲冲 KPI”
- Requests属不属于优秀的开源代码?
我们来先看看它的提交记录汇总,如下图:
(Requests 提交汇总)
基本上每月都会有 N 次提交,当然目前贡献者是来自全球各个地方的 Pythoneer,而最终由 kennethreitz 本人来做合并以保证代码的质量。
(Requests 最近提交记录)
这是 Requests 最近的提交记录,你会发现 kennethreitz 刚做过一次合并且目前最新的版本是 v2.18.1,其次可以看到 commit 的力度特别细、非常详细。
- 学习一份优秀的开源代码,如何开始?
我相信很多人都是直接拉到最新代码开始看,然后哼哧哼哧的啃上好几个月,最后也就有个大概的理解。
但,对于学习开源代码、梦想着自己也能写出来这么优秀代码的开发者来说,我不建议直接上手最新的代码。直接上手最新的代码,到后面你也只能了解到代码的调用逻辑。
- 举个例子:造飞机
如果你想知道飞机是怎么造起来、怎么飞起来的话,你可能只需要知道飞机上有哪些零件,这些零件的作用都是什么。但是如果让你造一架飞机出来,就拿你知道的那些毛皮知识根本没法造起来,这时候你就需要深入到这些零件的制造工艺和那些飞行动力学的原理。所以,你的第一步就需要去了解 莱特兄弟 当时造出来的第一架飞机,毕竟当时就一个空架子里面加了几个引擎和架子的设计、算法。掌握了从 0 开始的基础,然后你就可以尝试大步跳跃的了解之后的5代、10代、20代 乃至当今最牛逼的造飞机工艺。
- 开始着手学习
学习优秀的开源代码如同造飞机一样,我们需要从开源代码的最早版本开始,然后一步步去学习。
git tag # 可以罗列出当前代码的所有版本号
git checkout [tag_version] # 可以直接切换到对应版本的代码
当然,你也可以直接在 Github 上查看 tags 。
(Requests 版本列表)
这是 Requests 的版本列表,目前版本已经更新到了 v2.18.1 ,也就是说从 11 年 Requests 开源出来已经经历了无数个版本的迭代,才更新到目前相对比较成熟的版本。
那么我们先来切换到 v2.18.1 代码。
git checkout v2.18.1
(Requests v2.18.1源代码)
这是 Requests 的 v2.18.1 版本,也就是最新版本的源代码。这个版本中虽然只有这么十几个文件,但是大部分都是1k行的代码,对于新手来讲看起来非常费劲。
因此我这里强烈建议:如果你不是经常看源代码的老司机,那么就从一个开源项目的最早版本开始看。因为,一开始的版本都是非常简单且易懂,就拿 Requests 举例:
git checkout v0.2.0
我们将代码切换到 Requests 最早的版本。
(Requests v0.2.0 版本代码)
可以看到这是 kennethreitz 本人亲自提交的。其中只有 init.py 和 core.py 两个源代码,而且 core.py 中只是针对 urllib 做了一层封装,并将接口暴露之后。虽然第一个版本简单,但是有很多代码细节我们至少能一眼看到,并且学习到。
同样的方法,我们再直接跳到 V0.3.0的代码。
(Requests v0.3.0 版本代码)
你会发现 v0.2.0 到 v0.3.0 的改动并不多,只是增加了一个 packages 的目录,这个目录是直接引入第三方 poster 的库。其次就是增加了 async.py,这个文件是用来处理 Requests 并发问题,用到了 urllib, urllib2, eventlet 和 gevent 做兼容。最后就是代码风格的改动,更加符合 Pythonic 的语法。
那么接下来,我就分享下最近从 Requests 低版本中学到的知识。
二、注释
从 Requests 第一个版本开始,你就会发现有很详细的接口注释。
(Requests 中 Get 函数的代码片段)
Python 中的注释有多种形式,有单行注释、多行注释和批量注释。Python 注释有着自己的规范。
注释的意义就不多讲了,简单的来说就是提供给别人看懂你代码的帮助。
- 单行注释
井号(#)被用作单行注释符号,在代码中使用 # 时,它右边的任何数据都会被忽略。
- 批量、多行注释符号
在 Python 中也会有注释很多行的情况。这个时候就需要用到三引号 ''' ''' 包含。例如 Requests 的注释。
那么,对外提供的接口该如何注释?
在三引号 """ ... """ 中使用 冒号(:)后面紧跟 param + 参数名 : 参数的含义。这样就能很清晰的让别人理解你的代码。
(get 函数代码注释)
三、setattr、getattr、call
在 Requests 中最重要的两个类就是 Request 和 Response,Request 类决定了请求的参数并提供了请求的接口,而 Response 提供了请求结果的封装。
(Request 类的代码片段)
Request 这个类提供了几个参数:
其中有个方法 setattr ,是用来判断传入的请求协议是否属于 _METHODS 定义的那几种。若不在,则抛出无效方法的错误。
- setattr
用来设置对象的属性,通过 object 中的 setattr 属性来设置。
就像 requests 中,setattr 用来过滤不支持的请求方式。
- getattr
内建函数,可以通过函数名来获取。
因此,可以通过 'getattr' 来实现工厂模式
- call
用于实例自身的调用。也就是说如果定义了 call 方法,那么你的实例可以直接调用 () ,也就类似于 .call()。
四、Mixin模式
在我们写代码的过程中,不乏会出现很多需要代码复用的地方。也就类似于人类科技的进步,当你发现在同一件事上需要做很多遍的时候,那么这件事情必然可以被自动化,将操作过程复用以提高生产力。
所以,Mixin 是在 Python 乃至其他语言中定义的一种有利于代码复用的模式。
Mixin 本身其实是一种能力的承诺,它有部分或者全部的实现可被复用。
当然,在不同的语言或者框架中,对 Mixin 模式有不同的实现形式, Python 中除了 protocol,也可以用多继承的形式来实现Mixin,为了区别普通的多继承,同时 Mixin 类的类名一般都会带上后缀: " Mixin "。
比如在 Python Lib 里面的两个 Mixin 类:
UserDict.DictMixin
SocketServer.ForkingMixin。
我们再回到 Requests 源码,其中同样出现了 Mixin 的引入。在 v0.4.0 版本中针对 Response 的 Headers 就用到了 DictMixin。
(Requests 请求结果的代码片段)
其中 CaseInsensitiveDict 就继承自 DictMixin 。
(CaseInsensitiveDict 代码片段)
这样做的好处就是 Requests 可以直接将请求结果中的 Headers 数据转换为 Dict,同时也可以扩展 Headers 的函数。
所以,Mixin 其实是一种思想,用部分实现的接口来实现代码复用。可以解决多继承的问题,又可以用来扩展功能。
五、@staticmethod 和 @classmethod
- 简介
这两个关键字是 Python 内建的装饰器。一般来说,我们要使用某个类的方法的时候,需要先实例化一个对象再调用对应方法。
而如果函数中使用了 @staticmethod 和 @classmethod 来修饰,就可以不需要实例化之后再调用,直接类名.方法名() 来调用。这样有利于组织代码,把某些应该放在属于某个类的函数给放到那个类里面去,同时有利于命名空间的整洁。
- 区别
既然这两种装饰器 @staticmethod 和 @classmethod 都可以直接类名.方法名()来调用,那么它们的区别是什么呢?
从它们的使用上来看:
@staticmethod 不需要表示自身对象的 self 和自身类的 cls 参数,就和普通函数一样。
@classmethod 也不需要 self 参数,但第一个参数是表示自身类的 cls参数。
- 使用
有这么一种情况,我们的函数中有时候会用到类中的属性和方法。
针对这种情况,如果该函数是 @staticmethod 来修饰的,那么只能通过类名.属性名或者类名.方法名来调用。同样的,如果该函数是 @classmethod 修饰,由于该函数持有了 cls 参数,因此可以直接用来调用类的属性、类的方法、实例化对象等,避免了硬编码。
参考代码:
(@staticmethod 和 @classmethod 例子)
- Requests 中的用例
同时,我们来看看 Requests 中应用到的代码片段。
(Requests 中用到的 @staticmethod)
这是封装在 Request 类中的一个静态方法,用来生成开发者指定的请求URL 和 请求参数 data 格式化后的请求地址。
六、总结
看完了上面针对 Requests 低版本的学习分享,这时你也可以着手 Requests 低版本开始学习,我相信你会掌握更多 Python 中基础和高阶的知识点。
虽然,这些知识点看起来都很简单,但在实际开发中确实能帮助到我们很多。同时,又提高我们的代码水平,让我们代码更加优雅、更加风骚、更加 Pythonic。
后面的时间,我将会继续深入学习 Requests 源码,技术点也会更多,毕竟 Requests 代码如此的风骚。同时,我也将持续分享学习总结。
长摁‘识别二维码’,一起进步
生活不止眼前的苟且,还有手下的代码、
和嘴上的扯淡
——
个人博客: http://xiyoumc.0x2048.com
Github:https://www.github.com/xiyouMc