《Python高级编程》(第二版) ——速查笔记 第10~14章 完结
- 第十章 测试驱动开发 TDD test-driven Development
- 1. 简单的测试介绍
- 2.测试的提高部分
- 3.小结
- 第十一章 优化 一般分析原则与分析技术
- 1. 3个优化原则
- 2. 优化策略
- 3. 查找瓶颈
- 第十二章 优化 一些强大的技术
- 1. 降低复杂度
- 2. 简化
- 3. 使用集合模块
- 4. 架构体系的权衡
- 5. 缓存
- 6. 小结
- 第十三章 并发
- 1. 为何需要并发
- 2. 多线程
- 3. 多进程
- 4. 异步编程
- 5.小结
- 第十四章 有用的设计模式
- 1. 创建型模式 (creational patterns)
- 2. 结构型模式(structural patterns)
- 3. 行为模式(behavioral patterns)
第十章 测试驱动开发 TDD test-driven Development
1. 简单的测试介绍
- 为何使用TDD
- 可以在实际软件未开始,或者函数未实现的情况下,编写测试用例并不断壮大测试用例,以完成测试用例为目标来开发软件。
- 可以防止软件回归(新版本发生了旧版本发生的问题)因为是测试驱动开发,所以测试不可少,利于发现问题位置。
- 提高代码质量 将开发者关注点放在业务逻辑上,提高代码质量。
- 利用测试可以提高软件内部原理最好的解释
- 更快的编写更健壮的代码 使用TDD能让代码调试的时间减短,减少代码构建时间(局部测试、降低构建时间)
- 日常存在那些测试
- 验收测试 由客户或者客户目标构件的最终测试,通过这种测试后认为其具备交付条件。
- 单元测试 可以测试模块 函数 代码块等 最细颗粒度的测试
- 功能测试 针对某种功能的测试 在给定替代环境下检测单一功能的测试
- 集成测试,与功能测试相近比其测试的模块级别更高,也是在给定非生产环境下的测试。
- 负载和性能测试 web负载测试,只提供指标值不提供最终结果(需要人为判定)
- 代码质量测试 风格违例、文档数量 复杂性指标 静态代码分析警告等
- 常用测试工具
- unittest (单元\模块测试工具)
- unittest 标准库内组件
- 复写unittest.TestCase 类来创建测试案例 用 unittest.main()方法来调启测试。
类内各个测试 需要用 test_
开头,
- 可以写一个 test_suite方法来包裹多个测试的类,进行多个类的测试
2. doctest
-简单理解 特殊的rst文档中记录需要的函数和期望输出,以此来确定测试结果。
2.测试的提高部分
- unittest的问题
- 范板代码多,臃肿
- 扩展难
- 难以管理和组织复杂测试 分setup阶段和teardown阶段是绑定到testcase上 难以修改
- 较难收集测试结果组织测试
- 替代品 NOSE
- 使用 pip install nose 安装
- 使用 python -m nosetest -v在当前目录下搜索所有可以测试的类和函数(与unittest定义差不多)
- 编写测试固件(脚本) 可以编写 包 模块 测试 三个级别 三个附加函数setup固件加载前 teardown固件加载后 以及 自定义固件加载流程 用with_setup方法来加载相关方法
- 在setuptool 中 提供 test_suite="nose.collector" 值来集成nose
- 用的多还可以在测试文件夹中使用 .noserc 或者 nose.cfg 搞一些全局性配置
- 仍需遵守unittest的函数命名约定
- 替代品 py.test
- pip install pytest
- 同样自动搜索相应的测试类和函数。(注意大小写 写错了会停止测试)
- 编写测试固件 跟nose类似
会自动寻找相应的 setup_module/teardown 方法
- 可以用pytest.fixture()方法装饰测试函数可以使用上面的固件
- 可用 pytest.mark.skipif()方法跳过测试
- 还可以用 ssh驱动分布式测试。
4.代码覆盖率
- simply coverage来测试代码覆盖率
- 使用命令 coverage run -m unittest 来运行测试覆盖率
- 请注意覆盖率仅仅是测试的通过覆盖率,并不是所有的if else 分支都测试了
5. 仿真与模拟
- 仿真 就是写个跟无法获得类相同返回的类,模拟相关不可达返回创建测试可能性。
- 使用模拟
- pip install Mock
- 调用MagicMock方法来模拟API返回值 P288
6. 测试环境依赖兼容
- tox 建立测试矩阵 P290
7. 文档驱动开发
- 上文中提到的doctest 既测试的功能,又可以很好的解释整个软件的关键点。
3.小结
- unittest
- nose py.test
- 构建仿真和模拟
- 文档(测试)驱动开发
第十一章 优化 一般分析原则与分析技术
1. 3个优化原则
- 首先要能工作——别TM改了以后用不了
- 从用户角度考虑——用户关注的点不是你自己关注的点
- 保持代码的可读性和可维护性
2. 优化策略
别瞎猜,有对应的流程:
- 先找别的东西的原因 ——数据库 访问 I/O 等非代码问题
- 扩展硬件 硬件比人工便宜多了…
- 编写速度测试 就是time.time() -starttime
3. 查找瓶颈
- CPU瓶颈
- 分为 宏观分析 微观分析
- 宏观分析 使用cprofile 使用命令 Python3 -m cProfile myapp.py来进行分析 还可以函数内调用 或者配合 graphviz来做更精细的使用 p302
- 微观分析 当找到耗时较多的函数后 timeit查看多次运行的平均时间 使用test.pystones计算精确时间 p306
- 分析内存使用
- 解释并不给内存管理器的操作权
- 主要是要靠计数器
- ogjgraph p311-315
- 分析网络使用情况
- ntop wireshark等
- 小结
优化的3原则、优化策略、具体的一些分析方法及工具。
第十二章 优化 一些强大的技术
代码优化是一个迭代的过程,总的来说就是3个方法
1. 降低复杂度
- 循环复杂度 McCabe 复杂度(if while …等) 降低代码复杂度
- 大O记法 降低函数的大O的复杂度
2. 简化
选择优秀的数据存储方式
- bisect 有序列表查找为 O(logn) 注意有序性强弱
3. 使用集合模块
- deque 链表 list的链表代替
- defaultdict 在附值和查找稍微优化相比 dict
- namedtuple 使用tupe 可以用属性方法调用tupe元素 P326
4. 架构体系的权衡
- 是使用最佳算法 还是使用近似算法 TSP问题
- 使用队列处理延迟问题
- 使用概率型数据结构(大概率情况优化)
5. 缓存
- 确定的缓存 注意缓存占用
- 非确定性缓存 同步性与性能取舍
- memcached 缓存服务
6. 小结
- 降低算法复杂度的方法
- 架构层权衡 提高性能
- 缓存
第十三章 并发
1. 为何需要并发
并发不是并行,处理并发可以是并行(多进程)也可以是轮询(多线程)也可以是事务型(异步)
可以让界面马上可操作,可以让I/O阻塞型任务更高效,让部分多用户 分发模式的任务逻辑更简单
2. 多线程
同一个上下文环境中 不同的任务 就是一个线程
- Python中的多线程 有GIL 不能用多核加速 对I/O阻塞型事物,界面型事物有帮助,不能并行。
- python中多线程也是用一个解释器,其开销较多进程小
- 何时适合多线程
- 响应式页面
- 委派式工作
- 多用户应用程序
- 简单串行实例 P345
- 简单多进程 P347
- 线程池 Queue P348
- 双队列 (待处理que 和 输出que)p351
- 处理外部错误 p353
- 设置限制(人为制造瓶颈)p356
3. 多进程
每个进程有自己的上下文,不受GIL影响
- 使用multiprocessing模块 其中进程间通讯可以使用:p359
- Queue 同threading模块的queue
- Pipe 类似套接字的使用方法 p360
- sharedctypes 共享内存(只适用于ctype基本数据类型,尽量别用)
- 使用进程池 可以用 with Pool(12) as pool:的上下文管理模式来做 p362
- 可以使用 multiprocessing.dummy 中的Pool 来替代上面的真pool 从而将多进程改成多线程
4. 异步编程
核心就是自己控制释放资源的时间
- 协程 非抢占型的
- async 和await async修饰的函数返回一个future型的值(类似生成器) await方法交出程序控制权
- 异步实例 p371
- 无异步库/方法时候 如何类似异步的运行方法 Executor 和 futures
- 让所需异步运行的方法 在 Threadpoolexecutor 和 Processpoolexecutor 上面运行 然后用将结果用 await修饰 形成异步
5.小结
这里的知识都是Python程序员应该掌握的知识。
运用多个上述方法也是正常的
第十四章 有用的设计模式
1. 创建型模式 (creational patterns)
用于生成 具有特定行为的对象
- 单例模式
- 复写 类__new__方法 简单易于理解 容易出错 被继承后有麻烦
- 复写 metaclass 中的 __call__ 方法 常用
- 复写 类实例的.__dict__方法 (博格 borg 或者说教 单态 monostate)
- 最佳实践:**使用模块而非类**
2. 结构型模式(structural patterns)
有助于特定用例构建代码
- 适配器 duck-type
- **接口** 显式的声明ducktype 接口 A 依赖于接口 I B实现接口I 而不是 A依赖于B
- 使用 zope.interface 包 先定义一个interface 然后 用 @implementer(xxx)的方式来实现接口
- 抽象类ABC (Abstract Base Classes)除了跟上面差不多的接口继承,还可以复写 ABC类的 __subclasshook__方法来改变 isinstance的判断条件。
- 函数注解 类型提示
- collections.abs
- 代理模式 之前十二章的那种缓存就是代理 简单的说就是用一个更快 更节省的模式访问外部资源的方式
- 外观(facade) 在包级别以上更加简化其内部调用 (在包上面在包一层)
3. 行为模式(behavioral patterns)
- 观察者模式 在一个类中维护需要操作的观察者列表,当事件出现时候 向不同的观察者传递相应的参数 p386
- 访问者模式
- 模板
- 小结