前言

看书获心法,看大牛写的项目获形法。当我搜索,python源码阅读推荐,看到基本都有requests这个包,本身也经常用这个包,关键是这个包相对来说比较简单,就愉快的决定从这个轮子开始拆了。大体看完后,(〃´皿`)q膜拜Kenneth Reitz,狂打call,人帅,代码写得更帅,还是减肥励志级别人物。

感觉有趣,值得学习,借鉴的

目录结构

原先的目录文件结构并没有这样划分得这么清晰,只有一个core.py文件,里面包含了一切。这样划分的好处就是,相同功能的划分到一个文件里头,更加的清晰。这里提供了很好的文件命名规范,具体的如下注释。哈哈哈,以后自己写代码,目录结构按照这里的来,完美。

├── requests
│   ├── __init__.py
│   ├── adapters.py
│   ├── api.py              # 提供对外的api调用
│   ├── auth.py
│   ├── cacert.pem
│   ├── certs.py
│   ├── compat.py           # python2和python3兼容
│   ├── cookies.py
│   ├── exceptions.py       # 各种异常
│   ├── hooks.py
│   ├── models.py           # 代码中会用到的自定义类
│   ├── packages            # 存放第三方模块
│   │   ├── README.rst
│   │   ├── __init__.py
│   │   ├── chardet
│   │   └── urllib3
│   ├── sessions.py
│   ├── status_codes.py     # 全局各种状态码
│   ├── structures.py       # 自定义的容器类
│   └── utils.py            # 各种工具方法复制代码

优雅的hook函数

平时自己写函数,有时也会提供回调处理之类的,但是一般属于写死型,不够通用。在v0.6.0版本中看到,以下用法时,Σ(゚д゚lll)目瞪口呆,卧槽,强,牛逼。核心思路是1. 若有hook函数就处理,没有就返回原有数据,2. 利用**kwargs可以传入各种不同的参数(不用args估计是因为让参数意义更明确)。写一个通用 利用上partial,改造一下,就能变化出各种不同场景的hook处理了。

# v2.9.2 版本的,比起最初版,增加了点判断,思路是一样的。HOOKS = ['response']   # 限定dispatch_hook所能处理的hook函数def default_hooks():return dict((event, []) for event in HOOKS)# TODO: response is the only one# 这函数,若有hook函数就处理,没有就返回原有数据。这个没有就返回原来的数据很重要!!!调用时就可以不用判断,直接写写就行了。
def dispatch_hook(key, hooks, hook_data, **kwargs):"""Dispatches a hook dictionary on a given piece of data."""hooks = hooks or dict()hooks = hooks.get(key)if hooks:if hasattr(hooks, '__call__'):  hooks = [hooks]for hook in hooks:_hook_data = hook(hook_data, **kwargs)if _hook_data is not None:hook_data = _hook_datareturn hook_data# v0.6.0用法, 最初版的更能体会到dispatch_hook的强大。
args = dispatch_hook('args', hooks, args)r = Request(**args)# Pre-request hook.
r = dispatch_hook('pre_request', hooks, r)# Send the HTTP Request.
r.send()# Post-request hook.
r = dispatch_hook('post_request', hooks, r)复制代码

对于状态码是数字,但又想代码意义明确的优雅处理

以前写代码经常会这种反人类的写法if status == 1: do something。之后将它改进游戏,在文件开头用大写的变量定义状态,然后引入。但是看到下面的用法时,我看到了更加优雅的解决办法。核心思路:1. 将各种状态码写入一个文件 2. 用属性名来代替数字状态码


_codes = {# Informational.100: ('continue',),101: ('switching_protocols',),102: ('processing',),103: ('checkpoint',),122: ('uri_too_long', 'request_uri_too_long'),200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),  # 支持多种映射66666201: ('created',),202: ('accepted',),203: ('non_authoritative_info', 'non_authoritative_information'),204: ('no_content',),205: ('reset_content', 'reset'),206: ('partial_content', 'partial'),207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),208: ('already_reported',),226: ('im_used',),# 后面还有n多,果断省略}# 自定义的dict类似的容器类
class LookupDict(dict):"""Dictionary lookup object."""def __init__(self, name=None):self.name = namesuper(LookupDict, self).__init__()def __repr__(self):return '<lookup \'%s\'>' % (self.name)# python语言框架调用的,实现了这个就可以obj["item"]这样调用。典型的面向接口编程哲学思想def __getitem__(self, key):# We allow fall-through here, so values default to Nonereturn self.__dict__.get(key, None)def get(self, key, default=None):return self.__dict__.get(key, default)codes = LookupDict(name='status_codes')for code, titles in _codes.items():for title in titles:setattr(codes, title, code)if not title.startswith('\\'):setattr(codes, title.upper(), code)# 然后就可以这样用了if response.status_code == codes.see_other and method != 'HEAD'passif response.status_code == codes['see_other'] and method != 'HEAD'pass复制代码

一个兼容python2与python3的思路

一个名为compat.py的文件吸引了我的眼球,兼容总给我一种这是高大上的用法的感觉。里面给出了一个兼容2和3的思路。python2与3大体上的不同点1. 部分包名路径设置名字变了 2. 字符串,整形等基础数据类型的改变。而compat.py的核心思路是:将不同的弄成一样,然后其他文件,从该文件import。


if is_py2:from urllib import quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, proxy_bypassfrom urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefragfrom urllib2 import parse_http_listimport cookielibfrom Cookie import Morselfrom StringIO import StringIOfrom .packages.urllib3.packages.ordered_dict import OrderedDictbuiltin_str = strbytes = strstr = unicodebasestring = basestringnumeric_types = (int, long, float)elif is_py3:from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefragfrom urllib.request import parse_http_list, getproxies, proxy_bypassfrom http import cookiejar as cookielibfrom http.cookies import Morselfrom io import StringIOfrom collections import OrderedDictbuiltin_str = strstr = strbytes = bytesbasestring = (str, bytes)numeric_types = (int, float)复制代码

利用类来做上下文管理

上下文管理又是一个高级用法。最初的session的管理是用装饰器来做的,每个字母我都认识,但我完全看不懂!!!!但大神就大神,后来改用类来做,代码优雅度,可读性上升N个台阶。核心思路:创建一个专门用来管理上下文的类,利用对象属性,在下次操作时,将需要继续使用的,传入函数中。描述得比较魔幻,需要配合代码来理解。


class Session(SessionRedirectMixin)def __init__():# 注释全部去掉了,self.headers = default_headers()self.auth = Noneself.proxies = {}self.hooks = default_hooks()self.params = {}self.stream = Falseself.verify = Trueself.cert = Noneself.max_redirects = DEFAULT_REDIRECT_LIMITself.trust_env = Trueself.cookies = cookiejar_from_dict({})  # 主要观察点cookies, 下次请求带上上次的self.adapters = OrderedDict()self.mount('https://', HTTPAdapter())self.mount('http://', HTTPAdapter())self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE)def prepare_request(self, request):..... 持续省略cookies = request.cookies or {}# Bootstrap CookieJar.if not isinstance(cookies, cookielib.CookieJar):cookies = cookiejar_from_dict(cookies)# 上次请求的cookies会被保存到self.cookies这个属性里面,然后下次请求时带上。merged_cookies = merge_cookies(merge_cookies(RequestsCookieJar(), self.cookies), cookies)# Set environment's basic authentication if not explicitly set...... 持续省略return p  # 实现了这两个方法,就可以with Session() as session:dosomethingdef __enter__(self):return selfdef __exit__(self, *args):self.close()复制代码

教科书式的类继承体系

讲真,看同事写的代码,自己写的代码,在类继体系这一块,普遍都做得不好,为了方便,经常是乱继承,导致代码过度耦合!!!在殿堂级神书《冒号课堂》,有两句话,值得背下下来。1. 提倡接口继承,慎用实现继承。2. 非抽象类不适合作基类。补充一下,mixin类就是带实现的接口,不应该被实例化使用,算是接口继承。

auth.py# 专门设计出来,用于抽象的基类
class AuthBase(object):"""Base class that all auth implementations derive from"""def __call__(self, r):raise NotImplementedError('Auth hooks must be callable.')class HTTPBasicAuth(AuthBase):....省略def __call__(self, r):r.headers['Authorization'] = _basic_auth_str(self.username, self.password)return rclass HTTPProxyAuth(HTTPBasicAuth):def __call__(self, r):r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password)return rclass HTTPDigestAuth(AuthBase):....继续省略def __call__(self, r):# Initialize per-thread state, if neededself.init_per_thread_state()# If we have a saved nonce, skip the 401if self._thread_local.last_nonce:r.headers['Authorization'] = self.build_digest_header(r.method, r.url)try:self._thread_local.pos = r.body.tell()except AttributeError:# In the case of HTTPDigestAuth being reused and the body of# the previous request was a file-like object, pos has the# file position of the previous body. Ensure it's set to# None.self._thread_local.pos = Noner.register_hook('response', self.handle_401)r.register_hook('response', self.handle_redirect)self._thread_local.num_401_calls = 1models.py
# 将一些子类会公用到的,做成mixin类。多重继承也非魔鬼啊。另外的,标准库也有很多mixin类,有兴趣,可以再去看看collections模块里面的用法class RequestEncodingMixin(object)passclass RequestHooksMixin(object):passclass PreparedRequest(RequestEncodingMixin, RequestHooksMixin)passclass Request(RequestHooksMixin)pass复制代码

一个设置参数默认值的思路

在方法里面设置,而非在参数里面设置。适合参数巨多的场景

def send(self, request, **kwargs):# kwargs.setdefault('stream', self.stream)kwargs.setdefault('verify', self.verify)kwargs.setdefault('cert', self.cert)kwargs.setdefault('proxies', self.proxies)
复制代码

更快速自定义容器类

在models.py中看到了一个class CaseInsensitiveDict(collections.MutableMapping): pass 这样的用法。一般嘛,自定义容器类,需要实现各种各样的magic方法,对外接口啊。做为一个懒人,每次自定义都有实现实在是麻烦,还可能会漏。官方提供collections模块来拯救世界,里面有很多已经定义好的抽象基类。只要实现了要求的magic方法(没有实现还会很贴心的报错,告诉你没有实现),那么可以使用相对于的接口。

最后的唠叨

看源码的思路,主要是看用法,具体的和网络相关详细而且细节的知识略过。目的不是为了学习网络相关的细节知识,所以略过,就算要学也不应该看代码来学,太零散没有价值,应该要去看相关的协议。

拆轮子:requests相关推荐

  1. 拆轮子系列--RxJava理解(一)--Map解析

    本系列文章如下: 拆轮子系列--RxJava前奏篇 拆轮子系列--RxJava理解(一)--Map解析 拆轮子系列--RxJava理解(二)--subscribeOn 拆轮子系列--RxJava理解( ...

  2. 拆轮子系列--RxJava理解(三)--observeOn

    本系列文章如下: 拆轮子系列--RxJava前奏篇 拆轮子系列--RxJava理解(一)--Map解析 拆轮子系列--RxJava理解(二)--subscribeOn 拆轮子系列--RxJava理解( ...

  3. 拆轮子系列之教你一步步写验证码控件

    拆轮子系列之教你一步步写验证码控件 前言 先看看效果 怎么样不错吧?别急下面我就一步一步的教你实现. 用到的知识点总结: 1.Canvas和pint的使用,我们用它画点,线,字 2.View的基本用法 ...

  4. 拆轮子-RxDownload2源码解析(三)

    本文为博主原创文章,未经允许不得转载 造轮子者:Season_zlc 轮子用法请戳作者链接 ↑ 前言 本文主要讲述 RxDownload2 的多线程断点下载技术. 断点下载技术前提 服务器必须支持按 ...

  5. 如何系统的自学python 知乎-应该怎样系统的学习Python标准库?

    先放个牛逼的学习资料:Python 3 Module of the Week,中文名<每周一个 Python 3 模块>.作者几乎实践了一遍全部标准库,并为每一个 API 补充了一段代码示 ...

  6. 如何系统的自学python-应该怎样系统的学习Python标准库?

    先放个牛逼的学习资料:Python 3 Module of the Week,中文名<每周一个 Python 3 模块>.作者几乎实践了一遍全部标准库,并为每一个 API 补充了一段代码示 ...

  7. Note.Ver_2019.1.23

    有一段时间没有来写博客了,刚打开csdn就发现收到一条消息,有个博友评论了我的博客,工程管理自学计算机,这个世界永远不缺少努力前行的人.先讲技术,再讲故事. 技术相关 一. 大数据 冬令营对大数据的整 ...

  8. 函数 tostring_Kotlin实战之Fuel的高阶函数

    Fuel 是一个用 Kotlin 写的网络库,与 OkHttp 相比较,它的代码结构比较简单,但是它的巧妙之处在于充分利用了 Kotlin 的语言特性,所以代码看上去干净利落. OkHttp 使用了一 ...

  9. 彻底理解OkHttp - OkHttp 源码解析及OkHttp的设计思想

    OkHttp 现在统治了Android的网络请求领域,最常用的框架是:Retrofit+okhttp.OkHttp的实现原理和设计思想是必须要了解的,读懂和理解流行的框架也是程序员进阶的必经之路,代码 ...

最新文章

  1. /sys目录下其他几个目录的生成
  2. R语言dplyr包cumall函数、cumany函数和cummean函数实战
  3. 09、组策略之软件分发(05)
  4. Python练习-迭代器-模拟cat|grep文件
  5. 416. 分割等和子集(JavaScript)
  6. js无限分级 树_js实现无限级树形导航列表效果代码
  7. HTML5 Canvas雨滴下落动画 超逼真
  8. 喜庆访问量达到10万
  9. API在公司全业务快速使用给信息安全带来了巨大挑战
  10. Java 生产环境 linux下汉字变方框解决
  11. SD/SDHC卡下载UBOOT 的注意事项
  12. 系统函数,频率响应定义
  13. AURIX TriCore学习笔记四:LwIP裸机移植
  14. 程序员日常照片大合集!快来大饱眼福!
  15. 会议签到web_基于Web的网络签到系统设计与实现
  16. 如何关闭Windows Server 2012的IE增强安全配置
  17. SAP ADM100-1.1之SAP系统架构
  18. 打印机故障——0x00000709错误
  19. CNN已老,GNN来了!清华大学孙茂松组一文综述GNN
  20. java先序遍历树(递归、堆栈)

热门文章

  1. Java中的继承:父类和子类的关系
  2. Oracle12C 怎样导入scott用户
  3. replace()替换文字
  4. oc的分类category
  5. BlogEngine学习一:操作符重载
  6. JAVA8给我带了什么——并流行和接口新功能
  7. dbMigration .NET 数据同步迁移工具
  8. Redisbook学习笔记(3)数据类型之字符串
  9. 谈表达式树的缓存(6):五种缓存方式的性能比较
  10. python的tarfile模块实例 python把文件夹压缩成tar格式文件的例子