Python工匠 | 全书要点汇总(2)

简介: 7 函数1、函数参数与返回相关基础知识不要使用可变类型作为参数默认值,用None来代替使用标记对象,可以严格区分函数调用时是否提供了某个参数

Python工匠 | 全书要点汇总(1):https://developer.aliyun.com/article/1407215

7 函数

1、函数参数与返回相关基础知识

  • 不要使用可变类型作为参数默认值,用None来代替
  • 使用标记对象,可以严格区分函数调用时是否提供了某个参数
  • 定义仅限关键字参数,可以强制要求调用方提供参数名,提升可读性
  • 函数应该拥有稳定的返回类型,不要返回多类型
  • 使用返回None的情况——操作类函数、查询类函数表示意料之中的缺失值
  • 在执行失败时,相比返回None,抛出异常更为合适如果提前返回结果可以提升可读性,就提前返回,不必追求“单一出口”

2、代码可维护性技巧

  • 不要编写太长的函数,但长度没有标准,65行算是一个危险信号
  • 圈复杂度是评估函数复杂程度的常用指标,圈复杂度超过10的函数需要重构
  • 抽象与分层思想可以帮我们更好地构建与管理复杂的系统
  • 同一个函数内的代码应该处在同一抽象级别

3、函数与状态

  • 没有副作用的无状态纯函数易于理解,容易维护,但大多是时候“状态”不可避免
  • 避免使用全局变量给函数增加状态
  • 当函数状态较简单时,可以使用闭包技巧
  • 当函数需要较为复杂的状态管理时,建议定义类来管理状态

4、语言机制对函数的影响

  • functools.partial()可以用来快速构建偏函数
  • functools.lru_cache()可以用来给函数添加缓存
  • 比起mapfilter,列表推导式的可读性更强,更应该使用
  • lambda函数只是一种语法糖,你可以使用operator模块等方式来替代它
  • Python语言里的递归限制较多,可能的话,请尽量使用循环来替代

8 装饰器

1、基础与技巧

  • 装饰器最常见的实现方式,是利用闭包原理通过多层嵌套函数实现
  • 在实现装饰器时,请记得使用wraps()更新包装函数的元数据
  • wraps()不光有保留元数据,还能保留包装函数的额外属性
  • 利用仅限关键字参数,可以很方便地实现可选参数的装饰器

2、使用类来实现装饰器

  • 只要是可调用对象,都可以使用装饰器
  • 实现了__call__方法的类实例可调用
  • 基于类的装饰器分为两种:“函数替换”与“实例替换”
  • “函数替换”装饰器与普通装饰器没什么区别,只是嵌套层数更少
  • 通过类来实现“实例替换”装饰器,在管理状态和追加行为上有天然的优势
  • 混合使用类和函数来实现装饰器,可以灵活满足各种场景

3、使用wrapt模块

  • 使用wrapt模块可以方便地让装饰器同时兼容函数和类方法
  • 使用wrapt模块可以帮你写出结构更扁平的装饰器代码

4、装饰器设计技巧

  • 装饰器将包装调用提前到了函数被定义的位置,它的大部分优点也源于此
  • 在编写装饰器时,请考虑你的设计是否能够很好地发挥装饰器的优势
  • 在某些场景下,类装饰器可以替代元类,并且代码更简单
  • 装饰器和装饰器模式截然不同,不要弄混它们
  • 装饰器里应该只有一层浅浅的包装代码,要把核心逻辑放在其它函数与类中

9 面向对象编程

1、语言基础知识

  • 类与实例的数据,都保存在一个名为__dict__的字典属性中
  • 灵活利用__dict__属性,能帮你做到常规做法难以完成的一些事情
  • 使用@classmethod可以定义类方法,类方法常用作工厂方法
  • 类与实例的数据,都保存在一个名为__dict__的字典属性中
  • 灵活利用__dict__属性,能帮你做到常规做法难以完成的一些事情
  • 使用@classmethod可以定义类方法,类方法常用作工厂方法super()函数获取的并不是当前类的父类,而是当前MRO链条里的下一个类
  • Mixin是一种基于多重继承的有效编程模式,用好Mixin需要精心设计
  • 元类的功能相当强大,但同时也相当复杂,除非开发一些框架类工具,否则你极少需要使用元类
  • 通过定义__init_subclass__钩子方法,你可以在某个类被继承时执行自定义逻辑

3、鸭子类型与抽象类

  • “鸭子类型”是Python语言最为鲜明的特点之一,在该风格下,一般不做任何严格的类型检查
  • 虽然“鸭子类型”非常实用,但是它有两个明显的缺点——缺乏标准和过于隐式
  • 抽象类提供了一种更灵活的子类化机制,我们可以通过定义抽象类来改变isinstance()的行为
  • 通过@abstractmethod装饰器,你可以要求抽象类的子类必须实现某个方法

4、面向对象设计

  • 继承提供了相当强大的代码复用机制,但同时也带来了非常紧密的耦合关系
  • 错误使用继承容易导致代码失控
  • 对事物的行为而不是事物本身建模,更容易孵化出好的面向对象设计
  • 在创建继承关系时应当谨慎。用组合来替代继承有时是更好的做法

5、函数与面向对象的配合

  • Python里的面向对象不必特别纯粹,加入用函数打一点儿配合,你可以设计出更好的代码
  • 可以想requests模块一样,用函数为自己的面向对象模块实现一些更易用的API
  • 在Python中,我们极少会应用真正的“单例模式”,大多数情况下,一个简单的模块级全局对象就够了
  • 使用“预绑定方法模式”,你可以快速为普通实例包装出类似普通函数的API

6、代码编写细节

  • Python的成员私有协议并不严格,如果你想标示某个属性为私有,使用单下划线前缀就够了
  • 编写类时,类方法排序应该遵循某种特殊规则,把读者最关心的内容摆在最前面
  • 多态是面向对象编程里的基本概念,同时也是最想大的思维工具之一
  • 多态可能的介入时机:许多类似的条件分支判断、许多针对类型的isinstance()判断

10 面向对象设计原则(上)

1、SRP

  • 一个类只应该有一种被修改的可能
  • 编写更小的类通常更不容易违反SRP
  • SRP同样适用于函数,你可以让函数和类协同工作

2、OCP

  • 类应该对修改关闭,对扩展开放
  • 通过分析需求,找到代码中易变的部分,是让类符合OCP的关键
  • 使用子类继承的方式可以让类符合OCP
  • 通过算法类与依赖注入,也可以让类符合OCP
  • 将数据与逻辑分离,使用数据驱动的方式也是时间OCP的好办法

11 面向对象设计原则(下)

1、LSP

  • LSP认为子类应该可以任意替代父类使用
  • 子类不应该抛出符类不认识的异常
  • 子类方法应该返回与父类一致的类型,或者返回父类返回值的子类型对象
  • 子类的方法参数应该和父类方法完全一致,或者要求更为宽松(注意与上一点区别
  • 某些类可能会存在隐式合约,违反这些合约也会导致LSP

2、DIP

  • DIP认为高层模块和底层模块都应该依赖于抽象
  • 编写单元测试有一个原则:测试行为,而不是测试实现
  • 单元测试不宜使用太多mock,否则需要调整设计
  • 依赖抽象的好处是,修改底层模块实现不会影响高层代码
  • 在Python中,你可以用abc模块来定义抽象类
  • abc外,你也可以用Protocol等技术来完成依赖倒置

3、ISP

  • ISP认为客户依赖的接口不应该包含任何它不需要的方法
  • 设计接口就是设计抽象
  • 写更小的类、更小的接口在大多是情况下是个好主意

12 数据模型与描述符

1、字符串相关协议

  • 使用__str__方法,可以定义对象的字符串值(被str()触发)
  • 使用__repr__方法,可以定义对象爱对调试友好的详细字符串值(被repr()触发)
  • 如果对象只定义了__repr__方法,它同时会用于替代__str__
  • 使用__format__方法,可以在对象被用于字符串模板渲染时,提供多种字符串值(被.format()触发)

2、比较运算符重载

  • 通过重载与比较运算符有关的6个魔法方法,你可以让对象支持==>=等比较运算
  • 使用functools.total_ordering可以极大地减少重载比较运算符的工作量

3、描述符协议

  • 使用描述符协议,你可以轻松实现可复用的属性对象
  • 实现了__get____set____delete__其中任何一个方法的类都是描述符类要在描述符里保存实例级别的数据,你需要将其存放在instance.dict里,而不是直接放在描述符对象上
  • 使用__set_name__方法能让描述符对象知道自己被绑定了什么名字

4、数据类与自定义哈希运算

  • 要让自定义类支持集合运算,你需要实现__eq____hash__两个方法
  • 如果两个对象相等,它们的哈希值也必须相等,否则会破坏哈希表的正确性
  • 不同对象的哈希值可以一样,哈希冲突并不会破坏程序正确性,单会影响效率
  • 使用dataclasses模块,你可以快速创建一个支持哈希操作的数据类
  • 要让数据类支持哈希操作,你必须指定frozen=True参数将其声明为不可变类型
  • 一个对象的哈希值必须在它的声明周期里保持不变

5、其他建议

  • 虽然数据模型能帮我们写出更Pythonic的代码,单切勿过度推崇
  • __del__方法不是在执行del语句时被触发,而是在对象被作为垃圾回收时触发
  • 不要使用__del__来做任何“自动化”的资源回收工作

13 开发大型项目

一些项目开发工具介绍

  • flake8:包含pycodestyle模块(检查代码风格规范)和pyflakes模块(检查代码的正确性)
  • isort:格式化import语句
  • black:严格的代码风格工具,使用black {filename}命令整理代码
  • mypy:静态类型检查工具

单元测试工具:TDD(测试驱动开发)是一种行之有效的工作方式

  • unittest:标准库模块。
  • pytest:一个开源的第三方单元测试框架,更符合python风格。
    @pytest.mark.parametrize可以编写参数化测试。


相关文章
|
9月前
|
SQL 自然语言处理 算法
Python工匠 | 全书要点汇总(1)
前言 Python前前后后我学了好几遍了,了解了Python中的许多用法,但都为快速入门之类的教程,基于“要干什么 --> 怎么做”的模式学习。而在动手编程的过程中,我常常感觉心有所惑,于是决定更系统地去学习、了解Python知识。
98 1
|
7月前
|
数据采集 机器学习/深度学习 运维
Python大佬耗费13年,始成400页《Python工匠》手册
Python 能干的事情实在太多了,掰着指头数有点不够用。 Web 开发、数据分析、网络爬虫、自动化运维、后台开发、机器学习....... 如果你知道主攻哪个方向,只需重点去学习。不过,不论哪个方向,Python 编程的核心知识都是需要掌握的。 但是今天咱们要讨论的一件事儿,是对于一些入门了的盆友,写了不少代码,一到面试就卡bug,又或者在项目中运用Python,代码不怎么样,却自我感觉良好,结果到处出漏洞,一堆的烂摊子。 我相信,这样的朋友肯定不少,可是目前市面上能帮助大家解决这种问题的书籍又非常稀缺,今天我就给大家推荐一本能帮助你解决燃煤之急的书籍。
|
设计模式 缓存 IDE
『Python 工匠』是什么?
『Python 工匠』这个系列文章,是我的一次小小尝试。它专注于分享 Python 编程中的一些偏『小』的东西。希望能够帮到每一位编程路上的匠人。 系列文章: • Python 工匠:善用变量改善代码质量 • Python 工匠:编写条件分支代码的技巧
109 0
|
2月前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!
|
2月前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。
|
2月前
|
Unix Linux 程序员
[oeasy]python053_学编程为什么从hello_world_开始
视频介绍了“Hello World”程序的由来及其在编程中的重要性。从贝尔实验室诞生的Unix系统和C语言说起,讲述了“Hello World”作为经典示例的起源和流传过程。文章还探讨了C语言对其他编程语言的影响,以及它在系统编程中的地位。最后总结了“Hello World”、print、小括号和双引号等编程概念的来源。
126 80
|
24天前
|
存储 缓存 Java
Python高性能编程:五种核心优化技术的原理与Python代码
Python在高性能应用场景中常因执行速度不及C、C++等编译型语言而受质疑,但通过合理利用标准库的优化特性,如`__slots__`机制、列表推导式、`@lru_cache`装饰器和生成器等,可以显著提升代码效率。本文详细介绍了这些实用的性能优化技术,帮助开发者在不牺牲代码质量的前提下提高程序性能。实验数据表明,这些优化方法能在内存使用和计算效率方面带来显著改进,适用于大规模数据处理、递归计算等场景。
58 5
Python高性能编程:五种核心优化技术的原理与Python代码
|
3月前
|
存储 索引 Python
Python编程数据结构的深入理解
深入理解 Python 中的数据结构是提高编程能力的重要途径。通过合理选择和使用数据结构,可以提高程序的效率和质量
173 59
|
2月前
|
Python
[oeasy]python055_python编程_容易出现的问题_函数名的重新赋值_print_int
本文介绍了Python编程中容易出现的问题,特别是函数名、类名和模块名的重新赋值。通过具体示例展示了将内建函数(如`print`、`int`、`max`)或模块名(如`os`)重新赋值为其他类型后,会导致原有功能失效。例如,将`print`赋值为整数后,无法再用其输出内容;将`int`赋值为整数后,无法再进行类型转换。重新赋值后,这些名称失去了原有的功能,可能导致程序错误。总结指出,已有的函数名、类名和模块名不适合覆盖赋新值,否则会失去原有功能。如果需要使用类似的变量名,建议采用其他命名方式以避免冲突。
53 14
|
2月前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
119 2

热门文章

最新文章