Python工匠——案例、技巧与工程实践

《Python工匠》阅读笔记

前言

从刚发售那会,就看到有几位推友在推荐。刚开始还没特别感兴趣,第一眼印象:这不就是与 Fluent Python 类似的题材嘛,Fluent Python 都那么厚了,还能整出什么花来吗?我就没有买。

但是几天后,萝莉老师突然艾特我,“小圆,强推这本书啊”。按捺不住安利,仔细一看,原来是蓝鲸的大佬写的(离职前我刚好一度想活水到蓝鲸,最后还是没鼓起勇气去勾搭),看着目录好像也挺有东西,就迅速入了一本。后来公司的项目赶进度,也就每天中午午休的时间翻一翻,就这么零零散散地看了三个月,整本书翻了两遍,最近终于看完了。

总体而言,这本书给我的感觉跟 Fluent Python、Effective C++ 差不多。读完确实有收获,而且都挺实用的,但大多属于锦上添花。没见过这些技巧不影响你糊 CRUD,但是看完了可以让你的 CRUD 更好看、更好维护,也更不容易趟坑。

阅读笔记

大部分我自己已经熟知的技巧我没有整理进来。

变量与注释

  • 如何命名变量,有一个可以参考的格式:定语+主语
  • 注释应当解释背后的设计思路、代码的目的,或者起领读作用,而非逐句翻译代码本身
  • 注释/文档应当着重描述函数的功能以及参数说明,不应过多描述函数的实现细节
  • 变量的定义尽量靠近使用的地方
  • 如有条件,可以实践先写注释,后写代码
    • 如果发现你无法清晰地为一个将要新增的函数书写注释,那这个函数的功能定位很可能过于杂糅

数值与字符串

  • 应当时刻注意浮点数的精度问题
    • 如有必要可以使用 decimal.Decimal
  • 布尔值也是数字,在计数等场景下可以简化代码逻辑
    • True == 1;False == 0
  • 在大多数情况下,程序应当优先接收、处理普通字符串而非字节串
  • 尽量避免“魔数”的存在——定义常量或枚举
  • 为了美化代码,多行字符串有非预期的缩进时,可以使用 textwrap.dedent 来去除

条件分支控制流

  • bisect 二分查找可以简化范围判断的重复代码

异常与错误处理

  • contextlib.contextmanager 配合 yield,可以简便地实现上下文管理器
  • 避免抛出抽象级别高于、低于当前模块的异常

循环与可迭代对象

  • 迭代器:必须同时实现 __iter____next__
  • 可迭代对象:只需要实现 __iter__
  • 通过修饰可迭代对象来优化循环
    • 类似于“漏斗”,层层筛选数据

函数

  • itertools.product 简化多层循环
  • itertools.islice(seq, start, end, step) 实现隔行处理
  • iter(callable, sentinel) 很少见的做法,当迭代到 sentinel 时停止
  • 用函数 return 来实现“快速从多层循环中跳出”
  • 如果需要实现有状态的装饰器,可以采用闭包或者类的方式来保存状态
  • radon 工具来计算圈复杂度

装饰器

  • 浅装饰器,深实现
    • 装饰器仅仅是组装各种内部模块形成的轻量逻辑

面向对象编程

  • RPC 应当让使用者清楚地认知到是 RPC
  • 抽象类的子类化机制
    • __subclasshook__:定义了判断一个类是否为本类子类的“鸭子类型”准则,该类无需拥有与本类的继承关系
      • 注意要遍历 mro
  • __init_subclass__ 派生子类的钩子
  • super() 不一定会调用自己的直接父类,而是按照最开始发起实例化的类的 mro 顺序依次触发
  • 继承不一定适用于所有情景,善用 Mixin 模式
  • 对事物的行为建模,而非对事物建模
  • 类方法的推荐组织顺序
    • __init__
    • 对外 API
    • 内部工具函数
    • 其他魔术方法
  • 用模块来实现单例

面向对象设计原则

SOLID 原则

  • Single responsibility principle 单一职责原则
    • 一个类应当仅有一个被修改的理由
    • 如何解决
      • 大类拆小类
  • Open-closed principle 开闭原则
    • 类应该对拓展开放,对修改封闭
      • 找出类里容易发生变动的地方
      • 可以在不修改某个类的前提下拓展其行为
      • 典型例子是 sorted(iterable, *, key)
    • 如何解决
      • 让易变的逻辑可被外部定义
      • 数据驱动
  • Liskov substitution principle 里氏替换原则
    • 所有子类应当可以任意替换其父类使用,且不会破坏程序原本的功能
  • Interface segregation principle 接口隔离原则
    • 客户不应该依赖任何它不使用的方法
    • 如何解决
      • 拆接口
  • Dependency inversion principle 依赖倒置原则
    • 高层模块不应该依赖底层模块,二者都应该依赖抽象
    • 如何解决
      • 加抽象层

数据模型与描述符

  • @functools.total_ordering 可以一键补全所有缺失的比较函数
    • 只需要实现 __eq__ 与任意一个大于小于判断函数
  • 描述符:控制类属性的创建、获取、删除操作
    • 很适合拿来写 ORM 框架!

发表评论

您的电子邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据