参考文献:https://l1nwatch.gitbook.io/writing_solid_python_code_gitbook/
原文有更多更深的实用建议,本文仅节选部分对个人有用且常用的建议进行记录,如有纰漏,欢迎指出。
紫色:常用且复杂,建议看原文介绍。
绿色:不常用但实用,建议看原文介绍。
第二章 编程惯用法
建议 9:数据交换值的时候不推荐使用中间变量
x, y = y, x
建议 16:分清 == 与 is 的适用场景
可以通过 id()
函数来看看变量在内存中具体的存储空间。
is 表示的是对象标示符(object identity),而 == 表示的意思是相等(equal)。is 的作用是用来检查对象的标示符是否一致的,也就是比较两个对象在内存中是否拥有同一块内存空间,它并不适合用来判断两个字符串是否相等。x is y
仅当 x 和 y 是同一个对象的时候才返回 True,x is b
基本相当于 id(x) == id(y)
。而 == 才是用来检验两个对象的值是否相等的,它实际调用内部 __eq__()
方法,因此 a == b
相当于 a.__eq__(b)
,所以 == 操作符也是可以被重载的,而 is 不能被重载。一般情况下,如果 x is y
为 True 的话 x == y
的值也为 True(特殊情况除外,如 NaN,a = floag('NaN')
,a is a
为 True,a == a
为 false)。
Python 中存在 string interning
(字符串驻留)机制,对于较小的字符串,为了提高系统性能会保留其值的一个副本,当创建新的字符串的时候直接指向该副本即可。
建议 17:考虑兼容性,尽可能使用 Unicode
对字符串进行解码和编码,其中 decode()
方法将其他编码对应的字符串解码成 Unicode,而 encode()
方法将 Unicode 编码转换为另一种编码,Unicode 作为转换过程中的中间编码。
第三章 基础语法
建议 23:使用 else 子句简化循环(异常处理)
当循环“自然”终结(循环条件为假)时 else
从句会被执行一次,而当循环是由 break
语句中断时,else
子句就不被执行。与 for
语句相似,while
语句中的 else
子句的语意是一样的:else
块在循环正常结束和循环条件不成立时被执行。
建议 27:连接字符串应优先使用 join 而不是 +
当用操作符 +
连接字符串的时候,由于字符串是不可变对象,其工作原理实际上是这样的:如果要连接如下字符串:S1+S2+S3+...+SN
,执行一次 +
操作便会在内存中申请一块新的内存空间,并将上一次操作的结果和本次操作的右操作数复制到新申请的内存空间,在 N
个字符串连接的过程中,会产生 N-1
个中间结果,每产生一个中间结果都需要申请和复制一次内存,总共需要申请 N-1
次内存,从而严重影响了执行效率,时间复杂度近似为 O(n^2)
。
而当用 join()
方法连接字符串的时候,会首先计算需要申请的总的内存空间,然后一次性申请所需内存并将字符序列中的每一个元素复制到内存中去,所以 join
操作的时间复杂度为 O(n)
。
建议 28:格式化字符串时尽量使用 .format
方式而不是 %
建议 31:记住函数传参既不是传值也不是传引用
对于 Python 函数参数是传值还是传引用这个问题的答案是:都不是。正确的叫法应该是传对象(call by object)或者说传对象的引用(call-by-object-reference)。函数参数在传递的过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,调用者和被调用者之间共享这个对象,而对于不可变对象,由于并不能真正被修改,因此,修改往往是通过生成一个新对象然后赋值来实现的。
第四章 库
建议 37:按需选择 sort()
或者 sorted()
建议 38:使用 copy 模块深拷贝对象
-
浅拷贝(shallow copy):构造一个新的复合对象并将从原对象中发现的引用插入该对象中。浅拷贝的实现方式有多种,如工厂函数、切片操作、copy 模块中的 copy 操作等。
-
深拷贝(deep copy):也构造一个新的复合对象,但是遇到引用会继续递归拷贝其所指向的具体内容,也就是说它会针对引用所指向的对象继续执行拷贝,因此产生的对象不受其他引用对象操作的影响。深拷贝的实现需要依赖 copy 模块的
deepcopy()
操作。
实际上在包含引用的数据结构中,浅拷贝并不能进行彻底的拷贝,当存在列表、字典等不可变对象的时候,它仅仅拷贝其引用地址。要解决上述问题需要用到深拷贝,深拷贝不仅拷贝引用也拷贝引用所指向的对象,因此深拷贝得到的对象和原对象是相互独立的。
建议 39:使用 Counter 进行计数统计
建议 40:深入掌握 ConfigParser
建议 47:使用 logging 记录日志信息
建议 48:使用 threading 模块编写多线程程序
建议 49:使用 Queue 使多线程编程更安全
第五章 设计模式
建议 50:利用模块实现单例模式
建议 53:用状态模式美化代码
第六章 内部机制
建议 55:__init__()
不是构造方法
建议 56:理解名字查找机制
建议 69:对象的管理与垃圾回收